How to audit S3 public access on AWS
Public S3 buckets are still one of the most common ways company data leaks. This is the practical checklist: how a bucket becomes public, how to prove which of yours are exposed with the AWS CLI and IAM Access Analyzer, and how to lock the door with Block Public Access and Terraform so it stays shut.
Updated 2026-07-20 · ~8 minute read
A bucket can go public three different ways
Auditing S3 exposure is not a single check — it is three, because S3 grants public access through three independent mechanisms. A bucket ACL can grant the built-in AllUsers group (anyone on the internet) or AuthenticatedUsers (any AWS account). A bucket policy can set Principal: "*" with no condition. And a bucket configured for static-website hosting is only private if Block Public Access is on. You have to look at all three, at both the account and bucket level, to say a bucket is genuinely closed.
Turn on account-level Block Public Access
The account-level setting is the master switch: it overrides every bucket in the account, so a future public policy or ACL simply cannot take effect. It has 4 independent flags — turn all of them on unless you have a specific reason not to. First read the current state (an empty or errored response means it has never been configured, which is the risk), then set all four.
Rejects PUT requests that would set a public ACL on a bucket or object.
Ignores any public ACLs already attached, so they grant no access.
Rejects bucket policies that grant public access.
Limits access on a public-policy bucket to AWS service principals and authorised users in the account.
Verify each bucket, and let S3 tell you if it's public
Each bucket also carries its own Block Public Access settings. You don't have to reason about policies by hand — S3 exposes a get-bucket-policy-status call that returns an IsPublic boolean it computes itself. Loop it over every bucket to get a one-line public/not-public report for the whole account.
Read the bucket policy and ACL directly
For anything flagged public — or anything you want to confirm manually — pull the policy and ACL and look for two tells: a statement with "Principal": "*" and no restricting condition, and any ACL grant to the AllUsers or AuthenticatedUsers group URIs.
Cross-check with IAM Access Analyzer
IAM Access Analyzer is an AWS service that identifies resources — including S3 buckets — shared with a principal outside your account or organisation. It is a valuable second, independent signal: it catches cross-account sharing that a simple "is it world-public?" check misses. Enable an external-access analyzer (console: IAM → Access Analyzer), then list its S3 findings.
Codify it so it can't regress
A one-time console fix drifts. Put both the account-level and per-bucket Block Public Access blocks in Terraform so any new bucket is private by default and a reviewer sees an exposure change in the plan.
Intentionally-public bucket (a static site)? Keep these blocks on and serve it through Amazon CloudFront with Origin Access Control, so the bucket stays private and only CloudFront can read it.
How CloudArq detects this for you
CloudArq runs the same logic on a schedule across every region so you never have to remember to sweep. Two of its 192 read-only checks cover this issue directly: public_s3 flags any bucket made public by an ACL or policy, and no_account_s3_block flags an account with no account-level Block Public Access. It reads configuration metadata only — never your objects — and each finding ships with the exact CLI and Terraform fix from the steps above. It detects and hands you the fix; it never changes your account.
Each finding carries a copy-paste CLI / Terraform fix. Read-only — CloudArq never auto-changes your account.
Frequently asked
- 01What actually makes an S3 bucket "public"?
- Three independent things: a bucket ACL that grants the AllUsers or AuthenticatedUsers group, a bucket policy with a wildcard Principal ("*") and no restricting condition, or a static-website configuration with Block Public Access turned off. Any one of them exposes objects. That is why you check ACLs, policies, and the Block Public Access settings — not just one of them.
- 02Should I just turn on account-level Block Public Access everywhere?
- For most accounts, yes — enabling all four settings at the account level is the safest default and it overrides per-bucket mistakes. The exception is a bucket that is intentionally public (for example, a static site served directly from the S3 website endpoint). The recommended pattern is to keep Block Public Access on and front the bucket with Amazon CloudFront using Origin Access Control, so the bucket itself stays private.
- 03How do I find every public bucket without checking each one by hand?
- Loop over aws s3api list-buckets and call get-bucket-policy-status for each — it returns an IsPublic boolean computed by S3 itself. For a second, independent signal, turn on IAM Access Analyzer, which reports buckets shared with principals outside your account or organisation. CloudArq runs both classes of check across every region on a schedule and groups the results for you.
- 04Will enabling Block Public Access break a CloudFront-fronted bucket?
- No. With Origin Access Control, CloudFront reaches the bucket through a scoped bucket policy that names the CloudFront service principal — that is not "public" access, so Block Public Access leaves it intact. Only buckets that rely on a wildcard-public policy or a public ACL are affected, which is exactly what you want to close.
- 05Is CloudArq read-only, and does it ever store my S3 objects?
- Yes to read-only, no to storage. CloudArq connects through a read-only IAM role with an ExternalId and reads only configuration metadata — bucket policies, ACLs, and the Block Public Access settings. It never reads, copies, or stores your S3 objects, credentials, or database contents. Findings are encrypted at rest with AES-256-GCM and hosted in the EU (Helsinki). It detects and hands you the fix; it never auto-changes your account.
- 06Which CloudArq checks flag public S3 exposure?
- public_s3 flags any bucket exposed by a public ACL or bucket policy, and no_account_s3_block flags an account that has not enabled account-level Block Public Access. Both are read-only checks and each finding ships with the exact CLI and Terraform steps to close the gap.