S3 is one of the most commonly used AWS services—and one of the most commonly misconfigured. Data breaches from exposed S3 buckets make headlines regularly, yet many organizations still struggle to implement proper security controls.
The good news is that AWS has made significant improvements to S3 security defaults and tooling. The challenge is knowing which controls to enable and how to configure them properly for your use case.
This guide covers the essential S3 security practices every organization should implement, from basic configurations to advanced protection strategies.
The Fundamentals: Block Public Access
As of April 2023, AWS blocks public access by default for new buckets. But if you have older buckets—or if someone disabled this setting—you may have exposed data.
Check Your Settings
Every bucket should have these Block Public Access settings enabled unless you have a specific, documented reason to allow public access:
- BlockPublicAcls: Prevents new public ACLs from being created
- IgnorePublicAcls: Ignores existing public ACLs
- BlockPublicPolicy: Prevents new public bucket policies
- RestrictPublicBuckets: Restricts access to AWS principals only
Account-Level Protection
Enable Block Public Access at the account level as an additional safeguard:
aws s3control put-public-access-block \
--account-id YOUR_ACCOUNT_ID \
--public-access-block-configuration \
BlockPublicAcls=true,\
IgnorePublicAcls=true,\
BlockPublicPolicy=true,\
RestrictPublicBuckets=true
This provides defense-in-depth: even if someone misconfigures a bucket, the account-level setting prevents public access.
Encryption: At Rest and In Transit
Server-Side Encryption (At Rest)
Enable default encryption on all buckets. AWS offers three options:
- SSE-S3: AWS-managed keys, simplest option
- SSE-KMS: Customer-managed keys via KMS, provides audit trail
- SSE-C: Customer-provided keys, maximum control but operational complexity
For most use cases, we recommend SSE-KMS with a customer-managed key:
{
"Rules": [
{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "aws:kms",
"KMSMasterKeyID": "arn:aws:kms:region:account:key/key-id"
},
"BucketKeyEnabled": true
}
]
}
Pro tip: Enable "Bucket Key" to reduce KMS costs by up to 99% for S3 workloads.
Encryption In Transit
Enforce HTTPS-only access with a bucket policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EnforceHTTPS",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::your-bucket",
"arn:aws:s3:::your-bucket/*"
],
"Condition": {
"Bool": {
"aws:SecureTransport": "false"
}
}
}
]
}
Access Control: IAM Over ACLs
S3 offers two access control mechanisms: ACLs (Access Control Lists) and IAM policies. ACLs are a legacy feature that can lead to confusion and misconfigurations.
Disable ACLs
For new buckets, choose "ACLs disabled" as the Object Ownership setting. This ensures all access is controlled through IAM policies.
For existing buckets, migrate to IAM-based access:
- Audit existing ACL grants
- Create equivalent IAM policies
- Set Object Ownership to "Bucket owner enforced"
Least Privilege IAM Policies
When granting S3 access, be specific:
Bad (too permissive):
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": "*"
}
Good (specific actions and resources):
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::app-data-bucket",
"arn:aws:s3:::app-data-bucket/public/*"
]
}
Logging and Monitoring
Enable Server Access Logging
S3 server access logs capture detailed records of requests made to your bucket:
aws s3api put-bucket-logging \
--bucket your-bucket \
--bucket-logging-status '{
"LoggingEnabled": {
"TargetBucket": "your-logging-bucket",
"TargetPrefix": "s3-access-logs/"
}
}'
Store logs in a separate bucket with strict access controls and a lifecycle policy for retention.
CloudTrail for API Activity
CloudTrail logs management events (bucket creation, policy changes) by default. For data events (object-level operations), enable CloudTrail data events:
aws cloudtrail put-event-selectors \
--trail-name your-trail \
--event-selectors '[{
"ReadWriteType": "All",
"DataResources": [{
"Type": "AWS::S3::Object",
"Values": ["arn:aws:s3:::sensitive-bucket/"]
}]
}]'
S3 Storage Lens
Enable S3 Storage Lens for organization-wide visibility into storage usage and activity patterns. The free tier provides 28 metrics; the advanced tier adds 35 more including data protection and access management metrics.
Versioning and Object Lock
Enable Versioning
Versioning protects against accidental deletion and provides a recovery mechanism:
aws s3api put-bucket-versioning \
--bucket your-bucket \
--versioning-configuration Status=Enabled
Important: Versioning increases storage costs. Implement lifecycle policies to expire old versions:
{
"Rules": [
{
"ID": "ExpireOldVersions",
"Status": "Enabled",
"NoncurrentVersionExpiration": {
"NoncurrentDays": 30
}
}
]
}
Object Lock for Compliance
For data that must be immutable (regulatory compliance, legal hold), use S3 Object Lock:
- Governance mode: Users with special permissions can delete objects
- Compliance mode: No one can delete objects until retention expires, including the root account
Object Lock requires versioning and must be enabled at bucket creation.
Cross-Region and Cross-Account Access
VPC Endpoints
For workloads in VPCs, use VPC endpoints to keep S3 traffic off the public internet:
aws ec2 create-vpc-endpoint \
--vpc-id vpc-12345 \
--service-name com.amazonaws.us-east-1.s3 \
--route-table-ids rtb-12345
Combine with bucket policies that restrict access to specific VPC endpoints:
{
"Condition": {
"StringEquals": {
"aws:sourceVpce": "vpce-12345"
}
}
}
Cross-Account Access
When sharing buckets across accounts, use IAM roles and bucket policies rather than ACLs. The resource-based policy should specify the external account's principal:
{
"Principal": {
"AWS": "arn:aws:iam::EXTERNAL_ACCOUNT:role/DataAccessRole"
}
}
Security Checklist
Use this checklist to audit your S3 security posture:
Basic Protection
- Block Public Access enabled at account level
- Block Public Access enabled on all buckets
- Default encryption enabled (SSE-KMS recommended)
- HTTPS-only access enforced via bucket policy
Access Control
- ACLs disabled (bucket owner enforced)
- IAM policies follow least privilege
- No wildcard principals in bucket policies
- Cross-account access uses IAM roles
Monitoring
- Server access logging enabled
- CloudTrail logging enabled for data events
- S3 Storage Lens configured
- Alerts for public access attempts
Data Protection
- Versioning enabled for critical buckets
- Object Lock for compliance data
- Lifecycle policies for cost optimization
- VPC endpoints for private access
Key Takeaways
- Block public access at both account and bucket levels
- Encrypt everything with SSE-KMS and enforce HTTPS
- Disable ACLs and use IAM policies exclusively
- Enable logging via server access logs and CloudTrail
- Use versioning with lifecycle policies for cost control
Security misconfigurations are just one type of issue hiding in your AWS accounts. Our scanner checks for security gaps alongside cost waste, giving you a complete picture. Start your free scan to see what we find.



