Happy Dussehra! May good always win. Happy Diwali! Wishing you a bright year ahead. Merry Christmas! Warm wishes from Vizag. Happy New Year! Here’s to a great year ahead.

Archive · AWS

S3 Now Encrypts New Objects by Default

Since 5 January 2023 every new S3 object is encrypted at rest with SSE-S3. What changes, what stays your choice, how to use SSE-KMS and Bucket Keys, and how to enforce and audit it.

4 min read

  • Amazon S3
  • SSE-S3
  • AWS KMS
  • S3 Bucket Keys
  • AWS CLI 2.9

From 5 January 2023 Amazon S3 applies server-side encryption with S3-managed keys (SSE-S3, the AES-256 algorithm) to all new objects in every bucket automatically, at no extra cost and with no performance difference. Previously you had to turn on default encryption bucket by bucket, and many teams forgot. Objects already stored keep whatever encryption they had (including none), and the change needs no action for new uploads.

What happens on a write

flowchart TB
  UP[PutObject request] --> HDR{Request names an encryption type?}
  HDR -->|SSE-KMS or SSE-C or explicit SSE-S3| USE[S3 uses the requested method]
  HDR -->|no header| BKT{Bucket default encryption configured?}
  BKT -->|yes: SSE-KMS or SSE-S3| DEF[Use the bucket default]
  BKT -->|no| NEW[Apply SSE-S3 automatically since Jan 2023]
  USE --> OBJ[(Encrypted object at rest)]
  DEF --> OBJ
  NEW --> OBJ
  OBJ --> READ[GetObject - S3 decrypts transparently<br/>for callers who are authorised]
  OBJ -.->|SSE-KMS only| KMSCALL[KMS Decrypt call needs kms permission on the key]

Check what you have

aws s3api get-bucket-encryption --bucket acme-assets-2022
# Buckets that never had default encryption configured now report SSE-S3 (AES256)

aws s3api head-object --bucket acme-assets-2022 --key backups/db.sql.gz \
  --query '{enc:ServerSideEncryption,key:SSEKMSKeyId}'

Older objects may show no ServerSideEncryption field because they were stored unencrypted. To find them at scale, enable S3 Inventory with the Encryption status field and query it with Athena, then re-encrypt by copying the object over itself:

aws s3 cp s3://acme-assets-2022/ s3://acme-assets-2022/ --recursive --sse AES256 --metadata-directive REPLACE

For big buckets use S3 Batch Operations (copy operation) rather than a script. Test on a prefix first, as copying changes the LastModified time, rewrites objects and, with versioning enabled, creates new versions that you pay to store.

The three server-side choices

Need Use
Encryption at rest, no key management SSE-S3 (the default now)
Your own key policy, key-level audit trail, grants, ability to disable a key SSE-KMS with a customer managed key
Lower KMS request cost on busy buckets SSE-KMS with an S3 Bucket Key
You must hold the key outside AWS and send it with each request SSE-C (not for the new default; fewer services support it)
Encrypt before data leaves your application Client-side encryption (AWS Encryption SDK)

SSE-S3 protects against someone getting hold of the physical media; it does not limit who may read objects, because S3 decrypts transparently for anyone with S3 permission. SSE-KMS adds a second control: the caller also needs permission on the KMS key, and every use is recorded in CloudTrail.

Configure SSE-KMS and a Bucket Key (which reduces KMS requests from per object to per bucket for a period, cutting request charges by up to roughly 99 percent according to AWS):

aws s3api put-bucket-encryption --bucket acme-assets-2022 \
  --server-side-encryption-configuration '{
    "Rules":[{
      "ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"aws:kms","KMSMasterKeyID":"arn:aws:kms:ap-south-1:111122223333:alias/shop-data"},
      "BucketKeyEnabled":true}]}'

Callers then need kms:GenerateDataKey and kms:Decrypt on that key (and the key policy must allow them). Cross-account readers need access in both the bucket policy and the key policy; forgetting the key policy is the most common cause of “Access Denied” with KMS.

Enforce your policy, not just the default

The new default does not stop someone from uploading with a different method. If your standard is SSE-KMS, deny everything else in the bucket policy:

{
  "Version": "2012-10-17",
  "Statement": [
    { "Sid": "DenyNonTLS", "Effect": "Deny", "Principal": "*", "Action": "s3:*",
      "Resource": ["arn:aws:s3:::acme-assets-2022", "arn:aws:s3:::acme-assets-2022/*"],
      "Condition": { "Bool": { "aws:SecureTransport": "false" } } },
    { "Sid": "RequireKMS", "Effect": "Deny", "Principal": "*", "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::acme-assets-2022/*",
      "Condition": { "StringNotEquals": { "s3:x-amz-server-side-encryption": "aws:kms" } } }
  ]
}

Be careful: with the RequireKMS statement, uploads that omit the header can be denied, because the condition sees no header even though the bucket default would apply. Test clients before rolling it out, or rely on bucket default encryption and use AWS Config rules (s3-bucket-server-side-encryption-enabled) and Security Hub to detect drift.

AWS has announced that from April 2023 new buckets will have S3 Block Public Access enabled and access control lists (ACLs) disabled by default (the “bucket owner enforced” setting). Review any automation that creates buckets and relies on public ACLs, and use bucket policies or CloudFront with origin access control instead.

Checklist

  1. Confirm all buckets report encryption, and find old unencrypted objects with S3 Inventory.
  2. Decide per data class: SSE-S3 for general data, SSE-KMS (with Bucket Keys) for sensitive or regulated data.
  3. Enforce TLS and the encryption standard in bucket policies, and keep Block Public Access on.
  4. Remember that encryption at rest is not access control. Grant least privilege and review with IAM Access Analyzer.
  5. Include the KMS key in your backup and disaster recovery plan: if the key is deleted, the data is gone.