← Back to the S3 Bucket Exposure Checker

Why S3 buckets end up publicly exposed

A bucket only becomes a problem when it is listable, readable, or writable by someone with no relationship to your AWS account. There are a few common, independent ways that happens — fixing one does not fix the others.

1. Block Public Access is off, or was never turned on

S3's account-wide and bucket-level Block Public Access setting is the single strongest control: even if a bucket ACL or policy is later misconfigured to grant public access, Block Public Access overrides it and keeps the bucket private regardless. AWS has defaulted new buckets to Block Public Access ON since April 2023, but any bucket created before then, or any account where this was explicitly disabled for one legitimate reason and never re-enabled, can still be exposed.

# Check the account-wide setting
aws s3control get-public-access-block --account-id YOUR_ACCOUNT_ID

# Check one bucket's own setting
aws s3api get-public-access-block --bucket my-bucket

# Turn it on for a bucket
aws s3api put-public-access-block --bucket my-bucket --public-access-block-configuration \
  BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true

2. A public-read grant left over from static website hosting

A bucket configured for public static website hosting needs a public-read grant — but that grant does not automatically scope itself to only the files meant to be public. If the same bucket later starts holding backups, exports, or logs alongside the website's assets, all of it inherits the same public-read grant. Move non-public data to a separate, private bucket rather than relying on object-level ACLs to carve out exceptions inside a public one.

3. A grant to the AuthenticatedUsers group

It is easy to read AuthenticatedUsers as "my own logged-in users" — it is not. It means any AWS account holder on the internet, with no relationship to your organisation at all. There is essentially no legitimate reason to grant this group anything; if a policy needs to scope access to your own team, use IAM roles or a bucket policy with a specific Principal, never this group.

4. Prefer bucket policies over ACLs entirely

AWS's current guidance recommends disabling ACLs altogether (S3 Object Ownership set to "Bucket owner enforced") and using IAM policies and bucket policies with explicit Principal elements instead — a policy is easier to audit than a set of per-object ACL grants, and it cannot be silently widened by a later object upload that specifies its own ACL.

# Disable ACLs entirely (recommended for any bucket that does not need them)
aws s3api put-bucket-ownership-controls --bucket my-bucket \
  --ownership-controls Rules=[{ObjectOwnership=BucketOwnerEnforced}]

If this checker already found an exposure

Turn on Block Public Access first (step 1) — it is the fastest, broadest fix and overrides any ACL or policy underneath it. Then remove the specific ACL grant or bucket policy statement this checker named. If the bucket was listable, treat every object it held as potentially already accessed: check CloudTrail data events (if enabled) for anonymous GetObject calls, and rotate any credential or secret that may have been stored as an object in that bucket.

Run the S3 Bucket Exposure Checker against your bucket name to see which of these, if any, apply right now. Only run it against a bucket you own or are authorised to test.