AWS S3 Security: Protecting Your Cloud Storage Like a Pro
Picture this: You’ve got important documents - compliance reports, customer data, financial records. You store them in S3 (Simple Storage Service), AWS’s cloud storage. But here’s the thing: by default, S3 buckets are private, but one misconfiguration can expose everything to the entire internet. I’ve seen it happen, and it’s not pretty. Let’s make sure it doesn’t happen to you.
What is S3, Really?
Think of S3 as a massive, infinite filing cabinet in the cloud. You can store:
- Files (documents, images, videos)
- Backups
- Website content
- Application data
- Compliance reports
Each “drawer” in this filing cabinet is called a bucket, and each file is called an object.
The S3 Security Model
S3 security has three main layers:
- Access Control - Who can access your buckets
- Encryption - Protecting data at rest and in transit
- Public Access - Controlling internet exposure
Let’s dive into each one.
Layer 1: Access Control (IAM Policies)
Remember IAM from our last post? S3 uses IAM policies to control access. Here’s a basic example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-secure-bucket",
"arn:aws:s3:::my-secure-bucket/*"
]
}
]
}
This policy allows someone to:
s3:ListBucket- See what files are in the buckets3:GetObject- Download files from the bucket
Real-world example: Your compliance tool needs to read reports from a bucket. This policy gives it read-only access to just that one bucket.
Common S3 Actions You’ll See
s3:GetObject- Read/download a files3:PutObject- Upload a files3:DeleteObject- Delete a files3:ListBucket- List files in a buckets3:GetBucketLocation- Find out where a bucket is stored
Layer 2: Bucket Policies
Bucket policies are like the rules posted on the filing cabinet itself. They’re attached directly to the bucket.
Here’s a bucket policy that allows public read access to a specific folder:
1
2
3
4
5
6
7
8
9
10
11
12
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-website-bucket/public/*"
}
]
}
Breaking it down:
Principal: "*"- Anyone (public access)Action: s3:GetObject- Can read filesResource- Only files in the/public/folder
Real-world example: You’re hosting a website. You want images in the /public/ folder to be accessible to everyone, but keep everything else private.
Layer 3: Block Public Access (The Safety Net)
This is your most important security setting! Block Public Access is like a master switch that prevents accidental public exposure.
1
2
3
4
5
# Enable Block Public Access (recommended for most buckets)
aws s3api put-public-access-block \
--bucket my-secure-bucket \
--public-access-block-configuration \
"BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"
What this does:
- Blocks new public ACLs (access control lists)
- Ignores existing public ACLs
- Blocks public bucket policies
- Restricts public access even if policies allow it
Real-world example: You’re setting up a bucket for compliance reports. You enable Block Public Access, then even if someone accidentally creates a public policy, it won’t work. Safety first!
Encryption: Protecting Your Data
Encryption is like putting your files in a safe. Even if someone gets access, they can’t read them without the key.
Encryption at Rest (Data Stored in S3)
S3 offers several encryption options:
1. SSE-S3 (Server-Side Encryption with S3-Managed Keys)
This is the simplest option - AWS manages everything:
1
2
3
# Enable encryption when uploading
aws s3 cp report.pdf s3://my-bucket/ \
--server-side-encryption AES256
Or set it as default for the bucket:
1
2
3
4
5
6
7
8
9
aws s3api put-bucket-encryption \
--bucket my-bucket \
--server-side-encryption-configuration '{
"Rules": [{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "AES256"
}
}]
}'
Real-world example: Your compliance tool generates reports and uploads them to S3. With SSE-S3 enabled, every file is automatically encrypted. No extra work needed!
2. SSE-KMS (Server-Side Encryption with AWS KMS)
More control, more security. You manage the encryption keys:
1
2
3
4
5
6
7
8
9
10
aws s3api put-bucket-encryption \
--bucket my-bucket \
--server-side-encryption-configuration '{
"Rules": [{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "aws:kms",
"KMSMasterKeyID": "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
}
}]
}'
When to use KMS:
- You need audit logs of who accessed the keys
- You need to meet compliance requirements (like HIPAA)
- You want to control key rotation
Encryption in Transit (Data Moving to/from S3)
Always use HTTPS! S3 supports TLS/SSL by default. Just make sure you’re using the HTTPS endpoint:
1
2
3
4
5
# Good - uses HTTPS
aws s3 ls s3://my-bucket --endpoint-url https://s3.amazonaws.com
# Bad - uses HTTP (insecure)
aws s3 ls s3://my-bucket --endpoint-url http://s3.amazonaws.com
Versioning: Your Safety Net
Versioning is like having a time machine for your files. If something gets deleted or corrupted, you can go back:
1
2
3
4
# Enable versioning
aws s3api put-bucket-versioning \
--bucket my-bucket \
--versioning-configuration Status=Enabled
Real-world example: Your compliance tool accidentally overwrites an important report. With versioning enabled, you can restore the previous version. Crisis averted!
Lifecycle Policies: Automatic Cleanup
Lifecycle policies automatically move or delete files based on rules. Think of it as an automatic filing system:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
"Rules": [
{
"Id": "DeleteOldReports",
"Status": "Enabled",
"Expiration": {
"Days": 90
},
"Filter": {
"Prefix": "reports/"
}
},
{
"Id": "MoveToGlacier",
"Status": "Enabled",
"Transitions": [
{
"Days": 30,
"StorageClass": "GLACIER"
}
],
"Filter": {
"Prefix": "archive/"
}
}
]
}
What this does:
- Files in
reports/are deleted after 90 days - Files in
archive/are moved to cheaper Glacier storage after 30 days
Real-world example: Your compliance tool generates monthly reports. After 3 months, you don’t need them taking up expensive storage. The lifecycle policy automatically deletes them. After 1 month, archive files move to cheaper storage.
Hands-On: Securing a Compliance Report Bucket
Let’s build a secure bucket for compliance reports step-by-step:
Step 1: Create the Bucket
1
aws s3 mb s3://compliance-reports-2025 --region us-east-1
Step 2: Enable Block Public Access
1
2
3
4
aws s3api put-public-access-block \
--bucket compliance-reports-2025 \
--public-access-block-configuration \
"BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"
Step 3: Enable Encryption
1
2
3
4
5
6
7
8
9
10
aws s3api put-bucket-encryption \
--bucket compliance-reports-2025 \
--server-side-encryption-configuration '{
"Rules": [{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "AES256"
},
"BucketKeyEnabled": true
}]
}'
Step 4: Enable Versioning
1
2
3
aws s3api put-bucket-versioning \
--bucket compliance-reports-2025 \
--versioning-configuration Status=Enabled
Step 5: Create a Bucket Policy (Read-Only for Compliance Tool)
Save this as bucket-policy.json:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowComplianceToolRead",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::YOUR_ACCOUNT_ID:role/ComplianceScannerRole"
},
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::compliance-reports-2025",
"arn:aws:s3:::compliance-reports-2025/*"
]
}
]
}
Apply it:
1
2
3
aws s3api put-bucket-policy \
--bucket compliance-reports-2025 \
--policy file://bucket-policy.json
Step 6: Test It!
1
2
3
4
5
# This should work (if you have the right IAM role)
aws s3 ls s3://compliance-reports-2025
# This should fail (public access is blocked)
curl https://compliance-reports-2025.s3.amazonaws.com/report.pdf
Common S3 Security Mistakes
Mistake 1: Public Buckets
Don’t do this:
1
2
3
4
5
6
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:*",
"Resource": "*"
}
This makes your entire bucket public. Anyone can read, write, or delete files!
Do this instead: Use Block Public Access and specific bucket policies.
Mistake 2: No Encryption
Don’t do this: Uploading sensitive files without encryption.
Do this instead: Always enable encryption, at minimum SSE-S3.
Mistake 3: Weak Access Controls
Don’t do this:
1
2
3
4
{
"Action": "s3:*",
"Resource": "*"
}
Do this instead:
1
2
3
4
5
6
7
8
9
10
{
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::specific-bucket",
"arn:aws:s3:::specific-bucket/*"
]
}
Mistake 4: Not Using HTTPS
Don’t do this:
1
2
# BAD - uses HTTP
s3_client = boto3.client('s3', endpoint_url='http://s3.amazonaws.com')
Do this instead:
1
2
# GOOD - uses HTTPS (default)
s3_client = boto3.client('s3')
Using S3 with Python (Boto3)
Here’s how your compliance tool would interact with S3 securely:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import boto3
from botocore.exceptions import ClientError
# Create S3 client (automatically uses HTTPS)
s3_client = boto3.client('s3')
def upload_compliance_report(bucket_name, file_path, object_name):
"""Upload a compliance report to S3 with encryption."""
try:
s3_client.upload_file(
file_path,
bucket_name,
object_name,
ExtraArgs={
'ServerSideEncryption': 'AES256',
'ContentType': 'application/pdf'
}
)
print(f"Successfully uploaded {object_name} to {bucket_name}")
except ClientError as e:
print(f"Error uploading file: {e}")
return False
return True
def download_compliance_report(bucket_name, object_name, local_path):
"""Download a compliance report from S3."""
try:
s3_client.download_file(bucket_name, object_name, local_path)
print(f"Successfully downloaded {object_name}")
except ClientError as e:
print(f"Error downloading file: {e}")
return False
return True
def list_reports(bucket_name, prefix='reports/'):
"""List all compliance reports in a bucket."""
try:
response = s3_client.list_objects_v2(
Bucket=bucket_name,
Prefix=prefix
)
if 'Contents' in response:
return [obj['Key'] for obj in response['Contents']]
return []
except ClientError as e:
print(f"Error listing objects: {e}")
return []
# Example usage
if __name__ == "__main__":
bucket = "compliance-reports-2025"
# Upload a report
upload_compliance_report(
bucket,
"compliance_report_2025.pdf",
"reports/compliance_report_2025.pdf"
)
# List all reports
reports = list_reports(bucket)
print(f"Found {len(reports)} reports")
# Download a report
download_compliance_report(
bucket,
"reports/compliance_report_2025.pdf",
"downloaded_report.pdf"
)
Monitoring S3 Access
Want to know who’s accessing your buckets? Enable CloudTrail and S3 access logging:
1
2
3
4
5
6
7
8
9
# Enable server access logging
aws s3api put-bucket-logging \
--bucket compliance-reports-2025 \
--bucket-logging-status '{
"LoggingEnabled": {
"TargetBucket": "compliance-reports-2025-logs",
"TargetPrefix": "access-logs/"
}
}'
This creates a log of every request to your bucket. Perfect for compliance audits!
Key Takeaways
- Always enable Block Public Access - Your safety net
- Use encryption - At minimum SSE-S3, consider KMS for sensitive data
- Follow least privilege - Only give access to what’s needed
- Enable versioning - For important data
- Use lifecycle policies - To manage costs and cleanup
- Monitor access - Enable logging for compliance
- Always use HTTPS - Encrypt data in transit
Practice Exercise
Try this yourself:
- Create a new S3 bucket
- Enable Block Public Access
- Enable encryption (SSE-S3)
- Create a bucket policy that allows only your IAM user to read/write
- Upload a test file
- Try to access it publicly (should fail!)
Resources to Learn More
What’s Next?
Now that you understand S3 security, you’re ready to:
- Learn about EC2 security groups (our next post!)
- Understand VPC networking
- Build secure applications that store data in S3
Remember: S3 is powerful, but with great power comes great responsibility. Always think about security first!
💡 Pro Tip: Use AWS Config to automatically check if your S3 buckets have public access enabled. It’s like having a security guard that never sleeps, constantly checking your configurations!
Ready for more? Check out our next post on EC2 Security Groups, where we’ll learn how to control network access to your virtual servers!