S3 SecurityBucket PolicyData SecurityIAMAWS Config

AWS S3 Bucket Policy Audit: Finding and Fixing Over-Permissive Access

Vigilare Engineering

Platform Team · February 1, 2026 · 9 min read

The S3 Misconfiguration Problem

S3 bucket misconfigurations have been responsible for some of the largest data breaches in cloud computing history. Billions of records — health data, financial records, personal information — exposed through buckets that were either accidentally made public or had policies that granted unintended access to third parties. AWS has made significant improvements in defaults (Block Public Access is now the default for new buckets), but the problem persists in older buckets and in situations where teams disable protections without understanding the implications.

This guide covers the systematic approach to auditing S3 bucket policies — both the technical process and the organizational workflow for remediating findings.

The S3 Access Control Model

Before auditing, understand the layers of S3 access control:

  1. Block Public Access: Account-level or bucket-level setting that overrides ACLs and bucket policies to prevent public access. This is the bluntest instrument and the first line of defense.
  2. Bucket Policy: Resource-based JSON policy attached to the bucket, controlling access for any principal.
  3. IAM Policies: Identity-based policies on users, roles, and groups controlling S3 access.
  4. ACLs: Legacy access control mechanism. AWS recommends disabling ACLs in favor of bucket policies.

The audit must check all layers. A bucket with Block Public Access enabled but a bucket policy granting access to arn:aws:iam::PARTNER_ACCOUNT:root may be intended — or may be a forgotten permission from a relationship that's since ended.

Layer 1: Account-Level Block Public Access

Check account-level Block Public Access settings first:

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

All four settings should be true. If any are false, investigate why before proceeding. Account-level Block Public Access is rarely legitimately disabled. AWS Config rule: s3-account-level-public-access-blocks-periodic.

Layer 2: Bucket-Level Block Public Access

Even when account-level Block Public Access is enabled, individual buckets can override it (unless the account-level setting is enforced strictly). Check all buckets:

aws s3api list-buckets --query 'Buckets[].Name' --output text | tr '	' '
' | while read bucket; do
  result=$(aws s3api get-public-access-block --bucket "$bucket" 2>/dev/null)
  if [ $? -ne 0 ]; then
    echo "WARNING: $bucket - No Block Public Access configured"
  else
    echo "$result" | python3 -c "
import sys, json
config = json.load(sys.stdin)['PublicAccessBlockConfiguration']
if not all(config.values()):
    print(f'WARNING: $bucket - Block Public Access not fully enabled: {config}')
"
  fi
done

Layer 3: Bucket Policy Analysis

Bucket policy analysis is the most complex part of the audit. Policies can be permissive in subtle ways:

Automated Policy Analysis with IAM Access Analyzer

IAM Access Analyzer is the most reliable tool for finding external access in bucket policies. It analyzes all resource-based policies in your account and identifies any that grant access to principals outside your account:

aws accessanalyzer list-findings   --analyzer-arn arn:aws:access-analyzer:us-east-1:123456789012:analyzer/my-analyzer   --filter '{"resourceType": {"eq": ["AWS::S3::Bucket"]}}'

Any finding from Access Analyzer on an S3 bucket indicates external access — which may or may not be intentional. Each finding requires a human decision: is this access intentional and documented? If yes, archive the finding. If no, remove the access.

Manual Policy Review

For buckets that Access Analyzer flags, review the actual policy:

aws s3api get-bucket-policy --bucket my-bucket --query Policy --output text | python3 -m json.tool

Look for these high-risk patterns:

  • "Principal": "*" — Public access to anyone
  • "Principal": {"AWS": "*"} — Same as above
  • "Principal": {"AWS": "arn:aws:iam::EXTERNAL_ACCOUNT:root"} — Cross-account access
  • "Action": "s3:*" — All S3 actions (usually over-permissive)
  • Conditions with aws:PrincipalOrgID — May grant access to your entire AWS Organization (intended?)
  • Missing conditions on s3:PutObject — May allow unencrypted uploads

Finding Sensitive Data with Macie

For buckets that contain sensitive data, AWS Macie can scan for PII, financial records, credentials, and other sensitive information. If a bucket has a permissive policy AND contains sensitive data, remediation priority is higher. Enable Macie scans on buckets identified as over-permissive. See our guide on Macie data classification.

Remediating Over-Permissive Policies

After identifying over-permissive policies, remediation follows a pattern:

Remove Unnecessary Public Access

For most S3 buckets, public access is never appropriate. Enable Block Public Access at both the account and bucket level:

aws s3api put-public-access-block   --bucket my-bucket   --public-access-block-configuration "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"

Restrict Cross-Account Access

For legitimate cross-account access, add conditions that limit what the external account can do:

{
  "Effect": "Allow",
  "Principal": {"AWS": "arn:aws:iam::PARTNER_ACCOUNT:role/PartnerRole"},
  "Action": ["s3:GetObject"],
  "Resource": "arn:aws:s3:::my-bucket/partner-data/*",
  "Condition": {
    "StringEquals": {
      "aws:PrincipalOrgID": "o-YOURORGID"
    }
  }
}

See our S3 data exfiltration prevention guide and cross-account access guide for patterns.

Enforce Encryption on Uploads

Add a bucket policy condition that denies uploads without server-side encryption:

{
  "Effect": "Deny",
  "Principal": "*",
  "Action": "s3:PutObject",
  "Resource": "arn:aws:s3:::my-bucket/*",
  "Condition": {
    "StringNotEquals": {
      "s3:x-amz-server-side-encryption": "aws:kms"
    }
  }
}

See our S3 encryption guide for the full encryption configuration.

Automating the Audit with Config

AWS Config provides managed rules for S3 security:

  • s3-bucket-public-read-prohibited — Bucket should not allow public read access
  • s3-bucket-public-write-prohibited — Bucket should not allow public write
  • s3-bucket-server-side-encryption-enabled — Encryption should be enabled
  • s3-bucket-logging-enabled — Access logging should be enabled
  • s3-bucket-policy-grantee-check — Bucket policy should not grant access to specified principals

Enable these rules in all accounts and configure remediation for the highest-priority violations. See our Config rules guide and auto-remediation guide.

Ongoing Monitoring

S3 bucket policies change as teams add integrations, update applications, and onboard partners. Point-in-time audits are insufficient — you need continuous monitoring:

  • CloudTrail alert on PutBucketPolicy and PutBucketAcl events for sensitive buckets
  • Access Analyzer continuous analysis (findings appear within minutes of a policy change)
  • Config rules evaluated continuously after each change
  • S3 access logging to detect unexpected access patterns

See our guides on S3 security best practices and S3 access logging for the complete monitoring setup.

FAQ

How do I audit S3 policies across multiple accounts at scale?

Use AWS Organizations with delegated Access Analyzer in each account, aggregating findings to a central security account. For 10+ accounts, a custom Lambda that calls the S3 and Access Analyzer APIs in each account and aggregates results in DynamoDB provides more flexibility than the console.

What's the safest way to remediate a production bucket policy?

Never modify production bucket policies without testing. The process: create a test environment with similar permissions, apply the policy change there first, verify applications work, then apply to production. For truly sensitive changes, use the two-key IAM approach: create a new version of the policy, wait 24 hours to check for failures, then remove the old policy.

Should I use ACLs or bucket policies?

Disable ACLs for all new buckets (enable Object Ownership = Bucket Owner Enforced). Bucket policies provide more granular and auditable access control than ACLs. Existing buckets with ACLs should be migrated to policies during your next security review.

Protect your AWS accounts before it's too late

Vigilare monitors your AWS accounts for suspension risks — billing anomalies, IAM issues, GuardDuty findings, and more — and alerts you before AWS takes action.

Written by Vigilare Engineering

Platform Team