S3 Security, IAM Policies & Access Control
S3 security has multiple layers: Block Public Access settings (account and bucket level), bucket policies, IAM policies, ACLs (legacy), and encryption. The default is private — everything requires explicit grants.
Block Public Access — Always Check This First
# View account-level Block Public Access settings
aws s3control get-public-access-block --account-id 123456789012
# Bucket-level
aws s3api get-public-access-block --bucket my-bucket
# These 4 settings block public access regardless of bucket policy:
# BlockPublicAcls — ignores ACLs that grant public access
# IgnorePublicAcls — ignores any existing public ACLs
# BlockPublicPolicy — rejects bucket policies that grant public access
# RestrictPublicBuckets — restricts access to authorized users/services onlyBucket Policies
Bucket policies are resource-based IAM policies attached to the bucket. They can grant access to other AWS accounts, specific IAM roles, CloudFront, and anonymous users.
// Allow CloudFront OAC to read objects (modern approach)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCloudFrontServicePrincipal",
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/ABCDEF123456"
}
}
}
]
}// Grant another AWS account read access
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::999999999999:root"
},
"Action": ["s3:GetObject", "s3:ListBucket"],
"Resource": [
"arn:aws:s3:::my-bucket",
"arn:aws:s3:::my-bucket/*"
]
}
]
}# Apply bucket policy
aws s3api put-bucket-policy --bucket my-bucket --policy file://policy.json
# Get current policy
aws s3api get-bucket-policy --bucket my-bucket | jq '.Policy | fromjson'IAM Policies for S3 Access
// IAM policy: allow a specific app to read/write its prefix only
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
"Resource": "arn:aws:s3:::my-bucket/app-data/${aws:username}/*"
},
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::my-bucket",
"Condition": {
"StringLike": {
"s3:prefix": ["app-data/${aws:username}/*"]
}
}
}
]
}Encryption
SSE-S3 (default since Jan 2023): AWS manages keys, AES-256, no extra cost
SSE-KMS: your KMS key, audit trail via CloudTrail, extra API costs, per-request charges
SSE-C: you provide the key with every request, AWS never stores it
DSSE-KMS: dual-layer encryption with KMS (compliance use cases)
Client-side: encrypt before upload, AWS sees only ciphertext
# Upload with SSE-KMS
aws s3 cp sensitive.dat s3://my-bucket/ \
--sse aws:kms \
--sse-kms-key-id arn:aws:kms:us-east-1:123456789012:key/mrk-abc123
# Require encryption via bucket policy (deny unencrypted uploads)
# Add to bucket policy:
# "Condition": { "StringNotEquals": { "s3:x-amz-server-side-encryption": "aws:kms" } }S3 Object Lock (WORM)
Object Lock prevents objects from being deleted or overwritten for a fixed period or indefinitely. Must be enabled at bucket creation.
Governance mode: only users with s3:BypassGovernanceRetention can delete
Compliance mode: nobody can delete, not even root — use for strict compliance
Legal hold: indefinite, overrides retention period, toggle on/off
VPC Endpoints & Access Logging
# Create Gateway endpoint to keep S3 traffic inside VPC (free)
aws ec2 create-vpc-endpoint \
--vpc-id vpc-abc123 \
--service-name com.amazonaws.eu-west-1.s3 \
--route-table-ids rtb-xyz789
# Enable S3 access logging to another bucket
aws s3api put-bucket-logging --bucket my-bucket \
--bucket-logging-status '{
"LoggingEnabled": {
"TargetBucket": "my-logs-bucket",
"TargetPrefix": "s3-access-logs/my-bucket/"
}
}'Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free