6%
Of all S3 buckets globally are publicly accessible: representing billions of objects (UpGuard, 2025)
3
Independent permission layers that can make an S3 bucket publicly accessible
15 min
Time to run a complete S3 public exposure audit across all buckets in a single AWS account using the CLI
0
Cost to enable S3 Account-Level Block Public Access: the fastest way to close all three layers simultaneously

SponsoredRetool

Retool's new app builder is where AI-generated code ships safely

Building apps with AI is easy. Getting them to production safely is another story.

Start building for free today

S3 bucket public exposure is one of the most common causes of cloud data breaches: and one of the most commonly misunderstood permission systems in AWS. The confusion comes from the three overlapping layers of S3 access control, where each layer can independently expose data regardless of what the others say.

This guide covers all three layers, the CLI commands to audit each, and the preventive controls that close the exposure permanently.

The Three S3 Public Access Layers

Layer 1: Block Public Access (BPA)

Block Public Access is a meta-control that overrides bucket policies and ACLs. It exists at two levels: account-level (applies to all buckets) and bucket-level (applies to individual buckets). Account-level BPA settings take precedence: if account-level BPA is fully enabled, no individual bucket can be made public regardless of its policy or ACLs.

Block Public Access has four settings that can be independently enabled:

  • BlockPublicAcls: Ignores ACL grants that make objects public
  • IgnorePublicAcls: Makes existing public ACLs ineffective
  • BlockPublicPolicy: Prevents bucket policies that grant public access
  • RestrictPublicBuckets: Makes public bucket policies ineffective

A bucket with only BlockPublicAcls enabled (but not the other three) can still be made public via a bucket policy.

Layer 2: Bucket Policy

Bucket policies are JSON documents attached to individual buckets. A policy with Principal: "*" and Effect: Allow on any action grants that action to the entire internet, making the bucket publicly accessible regardless of BPA settings (unless BlockPublicPolicy or RestrictPublicBuckets BPA settings are enabled).

Layer 3: Object ACLs

Individual S3 objects can have ACLs that grant public-read or public-read-write permissions, overriding bucket-level settings. A bucket with a private policy can contain individual objects with public ACLs, making those specific objects publicly downloadable.

CLI Commands to Audit All Three Layers

Run these using the AWS CLI with an IAM user or role that has s3:GetBucketPublicAccessBlock, s3:GetBucketPolicy, and s3:GetBucketAcl permissions across all buckets.

Check account-level Block Public Access:

aws s3control get-public-access-block --account-id $(aws sts get-caller-identity --query Account --output text)

If all four settings show true, no bucket in the account can be public regardless of individual settings. If any setting shows false, proceed to check individual buckets.

List all buckets and check their BPA settings:

# List all buckets
aws s3api list-buckets --query 'Buckets[*].Name' --output text | tr '\t' '\n' > /tmp/buckets.txt

# Check BPA for each bucket
while read bucket; do
  echo -n "$bucket: "
  aws s3api get-public-access-block --bucket "$bucket" 2>/dev/null || echo "NO BPA CONFIG (fully public possible)"
done < /tmp/buckets.txt

Any bucket that returns NoSuchPublicAccessBlockConfiguration has no bucket-level BPA settings: its exposure is controlled only by account-level BPA (if enabled) and its policy/ACLs.

Check bucket policies for public access:

while read bucket; do
  policy=$(aws s3api get-bucket-policy --bucket "$bucket" --query Policy --output text 2>/dev/null)
  if echo "$policy" | grep -q '"Principal".*"\*"'; then
    echo "PUBLIC POLICY: $bucket"
    echo "$policy" | python3 -m json.tool | grep -A2 'Principal'
  fi
done < /tmp/buckets.txt

Find buckets flagged by AWS as public (uses the S3 console metadata):

aws s3api list-buckets --query 'Buckets[*].Name' --output text | tr '\t' '\n' | while read bucket; do
  status=$(aws s3api get-bucket-acl --bucket "$bucket" --query 'Grants[?Grantee.URI==`http://acs.amazonaws.com/groups/global/AllUsers`]' --output text 2>/dev/null)
  if [ -n "$status" ]; then
    echo "PUBLIC ACL: $bucket"
  fi
done
Free daily briefing

Briefings like this, every morning before 9am.

Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.

Automated Detection and Prevention

Manual auditing finds the current state. Automated detection catches future misconfigurations.

Enable S3 Account-Level Block Public Access (highest-impact single action):

aws s3control put-public-access-block \
  --account-id $(aws sts get-caller-identity --query Account --output text) \
  --public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true

This single command prevents any bucket in the account from being made public regardless of individual bucket settings. It does not affect buckets that need to serve public content (CloudFront origins, website hosting): those require using CloudFront with an Origin Access Control rather than direct public bucket access.

AWS Config rules for continuous monitoring:

Enable these two AWS Config rules to get automated alerts when S3 configuration violates your baseline:

# Detects buckets without all four BPA settings enabled
aws configservice put-config-rule --config-rule '{
  "ConfigRuleName": "s3-account-level-public-access-blocks",
  "Source": {"Owner": "AWS", "SourceIdentifier": "S3_ACCOUNT_LEVEL_PUBLIC_ACCESS_BLOCKS"}
}'

# Detects buckets with public read access
aws configservice put-config-rule --config-rule '{
  "ConfigRuleName": "s3-bucket-public-read-prohibited",
  "Source": {"Owner": "AWS", "SourceIdentifier": "S3_BUCKET_PUBLIC_READ_PROHIBITED"}
}'

These rules appear in Security Hub as control findings if Security Hub is enabled and the S3 standard is activated.

GuardDuty S3 protection: Enabling GuardDuty S3 Protection adds monitoring of S3 data event logs: it detects unusual data access patterns like bulk downloads, access from unusual geographic locations, and access by IAM principals that have never accessed the bucket before. Enable via: GuardDuty > Settings > S3 Protection > Enable.

When You Legitimately Need Public S3 Buckets

Some use cases require public access to S3 content. The secure pattern for each:

Static website hosting: Use CloudFront with an S3 Origin Access Control (OAC) instead of direct public bucket access. The bucket stays private; CloudFront serves as the public endpoint with HTTPS, DDoS protection, and WAF integration.

Public file downloads: Same pattern: CloudFront OAC with a private bucket. The CloudFront distribution serves files publicly while the bucket policy only grants access to CloudFront's service principal.

Public read for specific prefixes: If you must make specific objects (but not the entire bucket) publicly accessible, use a bucket policy with Resource: arn:aws:s3:::your-bucket/public/* instead of making the entire bucket public. Combine with BlockPublicPolicy: false only for the specific bucket where this is required, while keeping it true at the account level (account level does not override this when per-bucket settings exist).

The rule of thumb: no S3 bucket should have direct public access. CloudFront provides public access with caching, HTTPS, and WAF at lower cost than direct S3 public access for high-traffic content.

The bottom line

S3 public exposure requires checking three layers: Block Public Access settings (account and bucket level), bucket policies with public principal statements, and object-level ACLs. Checking only one layer produces a false sense of security. Enable account-level Block Public Access with all four settings to close all three layers simultaneously: this is the highest-impact single action. For buckets that legitimately serve public content, use CloudFront with Origin Access Control to keep the bucket private while serving content publicly.

Frequently asked questions

How do I check if an S3 bucket is public?

Check three layers: run 'aws s3api get-public-access-block --bucket BUCKET-NAME' to see BPA settings, 'aws s3api get-bucket-policy --bucket BUCKET-NAME' to check for policies with Principal: "*", and 'aws s3api get-bucket-acl --bucket BUCKET-NAME' to check for AllUsers grants. A bucket is publicly accessible if any layer permits public access without a BPA override.

What is the fastest way to make all S3 buckets private?

Enable account-level Block Public Access with all four settings: 'aws s3control put-public-access-block --account-id YOUR-ACCOUNT-ID --public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true'. This overrides all individual bucket settings and prevents any bucket from being made public.

What is AWS S3 Block Public Access and how does it work?

S3 Block Public Access (BPA) is a set of four settings that override bucket ACLs and bucket policies that would otherwise grant public access. The four settings: BlockPublicAcls prevents new public ACLs from being applied; IgnorePublicAcls ignores existing public ACLs; BlockPublicPolicy prevents bucket policies with public grants; RestrictPublicBuckets restricts access to bucket policy-based public grants. BPA can be set at the account level (applies to all current and future buckets) or at the individual bucket level. Account-level BPA takes precedence over bucket-level settings.

How do I find S3 buckets that have public access despite Block Public Access being enabled?

Block Public Access being enabled does not guarantee no public access. Check three things: (1) buckets with a BPA setting of BlockPublicPolicy=false can still have public bucket policies; (2) S3 Object Lambda Access Points can expose data without triggering standard BPA checks; (3) cross-account bucket policies granting access to specific accounts are not blocked by BPA (only public/anonymous access is). Run AWS Config rule s3-bucket-public-read-prohibited and s3-bucket-public-write-prohibited across all accounts to identify policy gaps that BPA does not cover.

How do I audit S3 bucket permissions across multiple AWS accounts?

Use AWS Security Hub with the Foundational Security Best Practices standard enabled: it runs S3 bucket configuration checks across all member accounts in your AWS Organization and reports failures in a centralized dashboard. For custom bulk auditing, use the AWS CLI: 'for bucket in $(aws s3 ls | awk {print $3}); do aws s3api get-public-access-block --bucket $bucket 2>&1; done'. IAM Access Analyzer can also identify S3 buckets accessible to external principals: enable it in each region and review its S3 bucket findings.

How do you configure S3 Block Public Access at the organization level and what residual risks remain after enabling it?

S3 Block Public Access can be enforced at the AWS Organizations level using a Service Control Policy (SCP) that denies s3:PutBucketPublicAccessBlock with a condition that sets the block to false, and denies s3:DeletePublicAccessBlock. This prevents any account in the organization from disabling the public access block regardless of IAM permissions. Additionally, configure the account-level S3 Block Public Access setting in AWS Organizations using the aws s3control put-public-access-block command targeting the management account -- this applies as a default for all member accounts. Residual risks after enabling Block Public Access: S3 bucket policies that grant access to authenticated AWS principals (any AWS account with valid credentials) are not blocked by Block Public Access -- only truly anonymous (unauthenticated) access is blocked. A bucket policy with Principal: * and a Condition restricting to aws:PrincipalOrgID is acceptable, but Principal: * without that condition still exposes the bucket to any authenticated AWS user. Also, S3 Pre-signed URLs bypass Block Public Access -- any object with a valid pre-signed URL is accessible to anyone with the URL. Audit pre-signed URL usage and expiration times in your applications.

Sources & references

  1. AWS S3 Block Public Access Documentation
  2. AWS Security Blog: S3 Public Access Controls
  3. AWS Config: s3-bucket-public-read-prohibited Rule

Free resources

25
Free download

Critical CVE Reference Card 2025–2026

25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.

No spam. Unsubscribe anytime.

Free download

Ransomware Incident Response Playbook

Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.

No spam. Unsubscribe anytime.

Free newsletter

Get threat intel before your inbox does.

50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.

Unsubscribe anytime. We never sell your data.

Eric Bang
Author

Founder & Cybersecurity Evangelist, Decryption Digest

Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.

Black Hat Giveaway

Win a $2,495 Black Hat pass.

Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.

Joins Decryption Digest daily briefing. Unsubscribe anytime.

Giveaway: Black Hat USA 2026 Full-Access Pass ($2,495 value)

Details →
Daily Briefing

Subscribe to enter the giveaway

Every subscriber is automatically entered. You also get daily threat intel every morning: zero-days, ransomware, and nation-state campaigns. Free. No spam.

Already subscribed? You're already entered.

Giveaway

Win a $2,495 Black Hat USA 2026 pass.