Locking down S3: block public access and enforce encryption
Turn on account-wide Block Public Access, require TLS and encryption with a bucket policy, and verify it.
Nearly every headline S3 breach is the same story: a bucket left public, objects uploaded without encryption, or traffic allowed over plain HTTP. AWS gives you layered controls — account-wide Block Public Access, bucket default encryption, and a bucket policy that denies insecure transport — and most incidents happen because someone skipped the first layer and assumed the bucket name was secret enough.
This note turns on account-level public access blocks, sets default SSE-S3 (or SSE-KMS where your org requires it), attaches a deny-insecure-transport policy, and verifies with CLI commands you can paste into a runbook. For how S3 fits into the wider AWS architecture exam lens, see AWS Solutions Architect Associate; for org guardrails and detective controls, see AWS security for DevOps.
Account BPA is the master switch. Bucket settings and policies add encryption and TLS requirements even when a future change tries to open the bucket.
Block public access at the account — then the bucket
Account-level Block Public Access overrides public bucket policies and ACLs. Turn all four settings on for the account first, then confirm each sensitive bucket inherits them. This is the control that stops a well-meaning Principal: * policy from actually exposing data.
aws s3control put-public-access-block \--account-id 111122223333 \--public-access-block-configuration \BlockPublicAcls=true,IgnorePublicAcls=true,\BlockPublicPolicy=true,RestrictPublicBuckets=trueaws s3api put-public-access-block \--bucket acme-app-data \--public-access-block-configuration \BlockPublicAcls=true,IgnorePublicAcls=true,\BlockPublicPolicy=true,RestrictPublicBuckets=true
Default encryption so clients cannot forget
Set default bucket encryption so every PutObject is encrypted even when the client omits ServerSideEncryption. SSE-S3 (AES256) is zero-config; SSE-KMS adds per-object key control and CloudTrail data events on KMS usage — pick what your compliance regime requires. There is no separate “deletion protection” toggle on KMS keys comparable to RDS; protect keys with key policies, IAM, and optional DeletionWindowInDays when scheduling key deletion.
aws s3api put-bucket-encryption --bucket acme-app-data \ --server-side-encryption-configuration \ '{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"AES256"}}]}'aws s3api get-bucket-encryption --bucket acme-app-dataSSEAlgorithm: AES256Deny cleartext and require encryption on write
A Deny on aws:SecureTransport = false blocks HTTP. Add a second statement denying PutObject when s3:x-amz-server-side-encryption is absent if you need to enforce KMS on every upload. Test policies with the IAM policy simulator before you attach — a typo in Resource ARNs fails open in ways that look fine in the console.
Enable S3 server access logging or CloudTrail data events on sensitive prefixes before you declare victory — detective controls tell you when a misconfigured lifecycle rule or cross-account copy bypassed your intent. Block Public Access stops accidental publicity; logging tells you when someone tried anyway.
{"Version": "2012-10-17","Statement": [{"Sid": "DenyInsecureTransport","Effect": "Deny","Principal": "*","Action": "s3:*","Resource": ["arn:aws:s3:::acme-app-data","arn:aws:s3:::acme-app-data/*"],"Condition": { "Bool": { "aws:SecureTransport": "false" } }},{"Sid": "DenyUnencryptedObjectUploads","Effect": "Deny","Principal": "*","Action": "s3:PutObject","Resource": "arn:aws:s3:::acme-app-data/*","Condition": {"StringNotEquals": {"s3:x-amz-server-side-encryption": "AES256"}}}]}
Verify with commands, not console eyeballing
aws s3api get-public-access-block --bucket acme-app-dataBlockPublicAcls: true (all four flags)aws s3api get-bucket-policy-status --bucket acme-app-dataIsPublic: falsecurl -I http://acme-app-data.s3.amazonaws.com/403 Forbidden — TLS-only policy blocks HTTPWhere this goes next
Bucket hardening is one resource type. Extend the same discipline to IAM roles that can read the bucket, enable S3 Object Lock for WORM workloads, and wire Checkov or AWS Config rules so a public ACL change fails the pipeline before apply. AWS security for DevOps covers Access Analyzer, SCPs, and detective controls across the account.
Go deeper in a courseAWS security for DevOps engineersS3, IAM, OIDC federation, and org-wide guardrails.View course