AWS S3 Bucket Misconfigurations: How Data Leaks Happen and How to Prevent Them
S3 Misconfigurations: Still the #1 Cloud Security Risk
Despite years of warnings, S3 bucket misconfigurations caused over 80% of cloud data breaches in 2025 (Qualys Cloud Security Report). Billions of records — medical data, financial records, credentials — exposed because of simple configuration errors.
High-Profile S3 Breaches
| Company | Records Exposed | Root Cause |
|---|---|---|
| Capital One | 106 million | SSRF + overpermissive IAM role |
| Twitch | Complete source code | Misconfigured S3 access |
| Hobby Lobby | 138GB customer data | Public bucket |
| Accenture | 40,000 passwords | Four public S3 buckets |
| U.S. Military | 1.8 billion social media posts | Unsecured S3 bucket |
Misconfiguration #1: Public Access via ACLs
# ❌ DANGEROUS: Making a bucket publicly readable
aws s3api put-bucket-acl --bucket my-data --acl public-read
# ❌ Even worse: Public write access
aws s3api put-bucket-acl --bucket my-data --acl public-read-write
Detection
# Check if any buckets have public ACLs
aws s3api get-bucket-acl --bucket BUCKET_NAME \
| jq '.Grants[] | select(.Grantee.URI == "http://acs.amazonaws.com/groups/global/AllUsers")'
# List ALL public buckets in your account
for bucket in $(aws s3api list-buckets --query 'Buckets[].Name' --output text); do
acl=$(aws s3api get-bucket-acl --bucket "$bucket" 2>/dev/null)
if echo "$acl" | grep -q "AllUsers"; then
echo "⚠️ PUBLIC: $bucket"
fi
done
Fix: Block Public Access at Account Level
# ✅ Block ALL public access for the entire AWS account
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
Misconfiguration #2: Overly Permissive Bucket Policies
// ❌ This policy allows ANYONE to read ALL objects
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/*"
}]
}
// ✅ Restrict to specific IAM role only
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:role/AppServerRole"
},
"Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::my-bucket/public/*"
}, {
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": "arn:aws:s3:::my-bucket/*",
"Condition": {
"Bool": { "aws:SecureTransport": "false" }
}
}]
}
Misconfiguration #3: No Encryption
# ❌ No server-side encryption — data at rest is unencrypted
aws s3 cp secret.txt s3://my-bucket/
# ✅ Enable default encryption with AWS-managed keys
aws s3api put-bucket-encryption --bucket my-bucket \
--server-side-encryption-configuration '{
"Rules": [{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "aws:kms",
"KMSMasterKeyID": "alias/my-s3-key"
},
"BucketKeyEnabled": true
}]
}'
Misconfiguration #4: No Versioning or Logging
Without versioning, a single delete command wipes data permanently. Without logging, you can't detect unauthorized access.
# ✅ Enable versioning
aws s3api put-bucket-versioning --bucket my-bucket \
--versioning-configuration Status=Enabled
# ✅ Enable server access logging
aws s3api put-bucket-logging --bucket my-bucket \
--bucket-logging-status '{
"LoggingEnabled": {
"TargetBucket": "my-access-logs-bucket",
"TargetPrefix": "s3-logs/my-bucket/"
}
}'
# ✅ Enable CloudTrail data events for the bucket
aws cloudtrail put-event-selectors --trail-name my-trail \
--event-selectors '[{
"ReadWriteType": "All",
"DataResources": [{
"Type": "AWS::S3::Object",
"Values": ["arn:aws:s3:::my-bucket/"]
}]
}]'
Misconfiguration #5: CORS Allowing All Origins
// ❌ Allows any website to read your S3 objects
{
"CORSRules": [{
"AllowedOrigins": ["*"],
"AllowedMethods": ["GET", "PUT", "POST"],
"AllowedHeaders": ["*"]
}]
}
// ✅ Restrict to your domains only
{
"CORSRules": [{
"AllowedOrigins": ["https://myapp.com", "https://staging.myapp.com"],
"AllowedMethods": ["GET"],
"AllowedHeaders": ["Authorization"],
"MaxAgeSeconds": 3600
}]
}
Automated Detection with Terraform
# Secure S3 bucket module
resource "aws_s3_bucket" "secure" {
bucket = "my-secure-bucket"
}
resource "aws_s3_bucket_public_access_block" "secure" {
bucket = aws_s3_bucket.secure.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_s3_bucket_server_side_encryption_configuration" "secure" {
bucket = aws_s3_bucket.secure.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
kms_master_key_id = aws_kms_key.s3.arn
}
bucket_key_enabled = true
}
}
resource "aws_s3_bucket_versioning" "secure" {
bucket = aws_s3_bucket.secure.id
versioning_configuration { status = "Enabled" }
}
S3 Security Checklist
- Account-level public access block enabled
- No bucket ACLs granting public access
- Bucket policies follow least privilege
- Default encryption enabled (SSE-KMS preferred)
- Versioning enabled on all data buckets
- Server access logging or CloudTrail data events enabled
- CORS restricted to specific origins
- MFA Delete enabled for critical buckets
- Lifecycle policies to expire/transition old objects
- Regular audits with AWS Config rules or ScoutSuite
Advertisement
Free Security Tools
Try our tools now
Expert Services
Get professional help
OWASP Top 10
Learn the top risks
Related Articles
Cloud Security Guide: AWS, Azure & GCP Misconfigurations 2025
Master cloud security with comprehensive guides on S3 bucket security, IAM policies, secrets management, and real breach case studies.
Cloud Security in 2025: Comprehensive Guide for AWS, Azure & GCP
Deep-dive into cloud security best practices across all three major providers. Covers IAM, network security, data encryption, compliance, and real-world misconfigurations that led to breaches.
Multi-Cloud Security Strategy: Unified Controls for AWS, Azure & GCP
87% of enterprises use multi-cloud. This guide provides a unified security strategy — identity federation, network segmentation, CSPM, centralized logging, and consistent policy enforcement across AWS, Azure, and GCP.