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:
- 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.
- Bucket Policy: Resource-based JSON policy attached to the bucket, controlling access for any principal.
- IAM Policies: Identity-based policies on users, roles, and groups controlling S3 access.
- 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 accesss3-bucket-public-write-prohibited— Bucket should not allow public writes3-bucket-server-side-encryption-enabled— Encryption should be enableds3-bucket-logging-enabled— Access logging should be enableds3-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
PutBucketPolicyandPutBucketAclevents 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