IAMSecurityAWS

AWS Resource-Based Policies: Security Implications and Best Practices

Vigilare Engineering

Platform Team · January 6, 2026 · 8 min read

Resource-based policies are attached to resources rather than identities. An S3 bucket policy, KMS key policy, SQS queue policy, or Lambda resource policy grants specific principals access to that resource without requiring role assumption. The principal can use their own identity to access the resource — it's the resource itself, not a role, that grants access.

This model is powerful and sometimes necessary (enabling cross-account access without managing roles in the target account), but it creates distinct security risks from identity-based policies. A misconfigured resource policy can expose a resource to unintended principals without any indication in the identity-based policy of the principal being granted access.

The Access Evaluation Difference

When evaluating whether a principal can access a resource, AWS evaluates both the identity-based policies attached to the principal and the resource-based policy on the resource. For same-account access, either the identity policy or the resource policy can grant access — if either allows it, the access is permitted (assuming no deny overrides). For cross-account access, both the identity policy and the resource policy must allow the access — one alone isn't sufficient.

This asymmetry has important security implications. For same-account access, a bucket policy that allows s3:GetObject for all principals in the account means any principal in the account can read from the bucket, regardless of what their identity policy says. For cross-account access, you need explicit grants in both places — the cross-account principal's identity policy must include the bucket access, and the bucket policy must allow the cross-account principal.

S3 Bucket Policies

S3 bucket policies are the most widely used resource-based policy type. Common security patterns:

Enforce HTTPS only:

{
  "Effect": "Deny",
  "Principal": "*",
  "Action": "s3:*",
  "Resource": ["arn:aws:s3:::my-bucket", "arn:aws:s3:::my-bucket/*"],
  "Condition": {"Bool": {"aws:SecureTransport": "false"}}
}

This deny prevents any principal from accessing the bucket over HTTP, enforcing HTTPS for all access. It's a defense-in-depth measure — even if code is misconfigured to use HTTP, the bucket policy rejects the request.

Restrict to specific VPCs: For buckets that should only be accessible from within your VPC (internal storage for application data), add a condition that restricts access to VPC endpoint IDs:

{
  "Condition": {"StringEquals": {"aws:SourceVpc": "vpc-0123456789abcdef0"}}
}

Public access patterns: For legitimately public buckets (hosting static websites or public downloads), the policy explicitly allows s3:GetObject for Principal: "*". This is intentional but must be deliberate. AWS S3 Block Public Access settings at the account and bucket level provide an additional gate — a bucket cannot become public through bucket policy alone if Block Public Access is enabled at the account level, requiring explicit per-bucket override.

KMS Key Policies

KMS key policies are required — unlike most AWS resources, KMS keys don't fall back to identity-based policies alone. A KMS key with no key policy is inaccessible. The default key policy created by the console grants the root account full access to the key, which is appropriate as a baseline.

When you want specific services to use a KMS key, add them to the key policy with appropriate permissions:

{
  "Effect": "Allow",
  "Principal": {"Service": "s3.amazonaws.com"},
  "Action": ["kms:GenerateDataKey", "kms:Decrypt"],
  "Resource": "*",
  "Condition": {"StringEquals": {"kms:CallerAccount": "123456789012"}}
}

The kms:CallerAccount condition ensures the service call originates from your account, not another account that might be using the same service endpoint.

IAM Access Analyzer for Resource Policies

IAM Access Analyzer identifies resource-based policies that grant access to external principals. Access Analyzer is specifically designed to surface the cross-account access risk inherent in resource policies. Enable Access Analyzer in every region and review findings regularly.

Access Analyzer findings categorize external access by type: allowing public access, allowing cross-account access from a specific account, or allowing access from any principal in a specific organization. Each finding includes the resource ARN, the principal being granted access, and the specific policy statement responsible. This makes it straightforward to determine whether each finding is intentional (cross-account access for a known partner account) or inadvertent (a bucket policy misconfiguration).

Detecting Dangerous Resource Policy Patterns

AWS Config rule s3-bucket-policy-not-more-permissive detects S3 bucket policies that are more permissive than a provided reference policy. For consistent enforcement of access patterns across your S3 fleet, define the maximum allowed permissions and use this rule to alert on deviations.

CloudTrail logs when resource policies are created or modified: PutBucketPolicy for S3, PutKeyPolicy for KMS, SetQueueAttributes for SQS. Monitor these events to catch unexpected policy changes. An alert when any S3 bucket policy is modified that includes Principal: "*" (public access) will catch accidental public bucket creation within seconds.

Related Reading

FAQ

Can a resource policy override an SCP?

No. SCPs apply to identity-based permissions — they limit what a principal can do even if a resource policy grants access. A resource policy can't grant permissions that an SCP denies. If an SCP denies s3:GetObject for principals in a specific OU, those principals cannot access S3 objects even if a bucket policy explicitly grants them access.

What's the difference between a bucket ACL and a bucket policy?

S3 bucket ACLs are an older access control mechanism that grants broad permissions to S3 principals (bucket owner, authenticated users, everyone). Bucket policies are JSON documents that provide fine-grained, condition-aware access control. AWS recommends using bucket policies instead of ACLs for new deployments. S3 Block Public Access settings include blocking new ACLs and public bucket policies as separate controls. Disable bucket ACLs entirely using the S3 Object Ownership setting, which centralizes all access control in bucket policies.

Do Lambda resource policies affect Lambda function security?

Lambda resource policies (function policies) control which services and accounts can invoke the function — they define the invocation permissions. They're separate from the execution role, which controls what the function can do. A function with a permissive resource policy that allows any AWS service to invoke it has a larger attack surface than one with tightly scoped invocation permissions. Audit Lambda resource policies as part of your security review process, using Access Analyzer to identify functions accessible from external accounts or any principal.

Protect your AWS accounts before it's too late

Vigilare monitors your AWS accounts for suspension risks — billing anomalies, IAM issues, GuardDuty findings, and more — and alerts you before AWS takes action.

Written by Vigilare Engineering

Platform Team