Resource policies and public data
A bucket policy says who can reach the bucket, and "*" means the whole internet. Name identities, scope prefixes, and turn on Block Public Access.
Who does the policy name?
A bucket policy is a resource policy. Its statements have a Principal, and that decides who the statement applies to. "Principal": {"AWS": "arn:aws:iam::123456789012:role/label-cdn"} means one role. "Principal": "*" means every caller, signed or not, from any account or none. A statement allowing s3:GetObject to "*" on bucket/* publishes every object in the bucket to anyone who can guess or find a key. Bucket names often leak through page source, error messages, and DNS.
How access adds up
Within one account, a request is allowed if the bucket policy or the caller's identity policy allows it, and nothing denies it. That gives you two ways to grant a role access: name the role in the bucket policy, or give the role an identity policy for the bucket. For callers in other accounts, both sides must allow. ACLs are an older, coarser mechanism. New buckets disable them, and they are best left that way. Scope by prefix as well as identity. A CDN that serves labels needs s3:GetObject on bucket/labels/*. It does not need the internal price list next to them, and it never needs to write.
Block Public Access
Block Public Access is the guard rail. BlockPublicAcls and IgnorePublicAcls stop public ACLs from being set or having any effect. BlockPublicPolicy makes S3 refuse a bucket policy that would grant public access. RestrictPublicBuckets limits an already-public policy to AWS services and principals in the account. Turn on all four for any bucket that is not deliberately a public website, ideally at the account level too. A guard rail does not replace a correct policy, but it turns the next "I'll just open it up" into an error message instead of a data breach.
Checking your work
After changing a policy, check it from both sides. As the role that needs access, read an object under its prefix, and try to read something outside it, which should fail. Without credentials, request an object directly by its URL, which should return Access Denied. aws s3api get-bucket-policy-status reports whether S3 considers the policy public, and IAM Access Analyzer can list every resource in the account that is shared outside it. Run that list now and again after every change. Public data is rarely published on purpose. It is usually a quick fix that nobody revisited.
Key terms
- Resource policy
- A policy attached to a resource, such as a bucket, that names which principals may act on it.
- Public grant
- A policy statement or ACL that gives access to everyone ("*", AllUsers) or to any authenticated AWS user.
- Block Public Access
- Bucket or account settings that make S3 ignore or refuse public ACLs and public bucket policies.
Read further
- Amazon S3 User Guide, Security → Identity and access management → "Bucket policies" (Free online)
The Principal element, resource ARNs for buckets and objects, and examples that grant one role access to one prefix. - Amazon S3 User Guide, Security → "Blocking public access to your Amazon S3 storage" (Free online)
The four settings, what S3 treats as "public", and why account-level settings exist. - AWS Identity and Access Management User Guide, Reference, "Policy evaluation logic", the section on resource-based policies (Free online)
Same-account access granted by either the resource policy or the identity policy.