Public Buckets Are Rarely Made Public Deliberately

Key takeaway: Cloud storage access is decided by the interaction of multiple policy layers. Exposure usually results from one layer granting what another was assumed to restrict.
Why the Model Confuses People
Access to an object can be affected by bucket policy, object ACL, bucket ACL, IAM identity policy, account-level block settings and, in organisation setups, service control policies.
Reasoning about the combination is genuinely difficult. A bucket policy denying public access may coexist with object ACLs granting it, and whether the object is reachable depends on evaluation precedence that most engineers have not memorised.
The common exposure paths are consistent across incidents:
- A grant to
AuthenticatedUsers, which means any AWS account holder rather than your organisation’s users - A wildcard principal in a bucket policy added to make an integration work
- Objects uploaded by a process that sets its own permissive ACL
- A bucket created before account-level blocking was enabled
- A CloudFront distribution or proxy exposing content the bucket itself restricted
The AuthenticatedUsers case is the most misunderstood. It reads as though it means logged-in users of your application. It means anyone with any account on the cloud provider, which is functionally public.
Controls That Actually Prevent It
| Control | Strength |
|---|---|
| Account-level public access block | Highest — overrides everything |
| Disable ACLs, use policies only | Very high — removes a whole layer |
| Organisation-level SCP denying public grants | Very high |
| Automated scanning and alerting | Detective, not preventive |
| Manual review at creation | Unreliable |
Enabling public access block at the account level is the single highest-value action. It overrides bucket and object settings, so a mistake at a lower layer cannot produce exposure.
Disabling ACLs entirely — using bucket-owner-enforced ownership — eliminates the layer responsible for most accidental exposure, because uploaded objects can no longer carry their own permissions.
Both of these are configuration changes rather than programmes. They are unglamorous and they close the majority of the risk.
Where Exposure Persists Anyway
Buckets that must be public — static website assets, published downloads — are legitimate. The failure mode is scope creep: a bucket created for public assets that later accumulates backups, logs or exports because it was the convenient place to put them.
Separate them structurally. Public content lives in dedicated buckets that contain nothing else, and everything else lives in buckets that cannot be made public. Mixing intent within a bucket is what turns a correct configuration into a breach.
Presigned URLs are the right mechanism for time-limited access to private objects, and they are frequently misused with excessive expiry times. A URL valid for a week is effectively a public link that happens to be long.
Detection as a Backstop
Automated scanning for public grants and alerting on changes to bucket policy remains necessary, because preventive controls can be disabled and new accounts can be created outside standard configuration. Treat a policy change on a data bucket as an event worth reviewing rather than a routine operation.
The Bottom Line
Enable account-level public access block and disable ACLs, which together prevent most accidental exposure structurally. Keep public and private content in separate buckets, keep presigned URL lifetimes short, and alert on bucket policy changes as a backstop.



