Skip to main content
emnode / learn
Compliance Medium severity

AWS Security Hub · S3

S3.17: Buckets should be encrypted at rest with KMS keys

Written and reviewed by Emnode · Last reviewed

What does AWS Security Hub S3.17 check?

S3.17 checks that a bucket's default encryption uses an AWS KMS key (SSE-KMS or DSSE-KMS) rather than the default SSE-S3. It reports FAILED when the bucket is encrypted only with SSE-S3 keys.

Why does S3.17 matter?

S3.17 treats SSE-S3 as a governance gap rather than an exposure: data is still encrypted, but with KMS you gain an auditable key policy, CloudTrail on every decrypt, and the ability to revoke access by changing the key. That control plane is what auditors and incident responders actually need.

How do I fix S3.17?

  1. Audit which buckets are still on SSE-S3 with get-bucket-encryption.
  2. Set default encryption to a customer-managed KMS key — existing objects are not re-encrypted, only new writes use the key.
  3. Enable S3 Bucket Keys to keep KMS request costs from ballooning on high-traffic buckets.
  4. Check the key policy grants the right principals kms:Decrypt to avoid AccessDenied surprises.

Remediation script · bash

# 1. Audit every bucket and flag the ones still on the default key (SSE-S3).
for b in $(aws s3api list-buckets --query 'Buckets[].Name' --output text); do
  alg=$(aws s3api get-bucket-encryption --bucket "$b" \
    --query 'ServerSideEncryptionConfiguration.Rules[0].ApplyServerSideEncryptionByDefault.SSEAlgorithm' \
    --output text 2>/dev/null)
  [ "$alg" != "aws:kms" ] && echo "FAIL S3.17: $b (current: ${alg:-none})"
done

# 2. Flip a bucket to a customer-managed KMS key WITH Bucket Keys (cost lever) enabled.
KEY=arn:aws:kms:us-east-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab
aws s3api put-bucket-encryption --bucket customer-records-prod \
  --server-side-encryption-configuration \
  '{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"aws:kms","KMSMasterKeyID":"'$KEY'"},"BucketKeyEnabled":true}]}'

# 3. Re-enable encryption on a CodeBuild project's S3 logs (CodeBuild.3).
aws codebuild update-project --name api-service-build \
  --logs-config '{"s3Logs":{"status":"ENABLED","location":"my-build-logs-bucket/api-service","encryptionDisabled":false}}'

# 4. Migrate existing objects in place so the whole dataset sits behind the controlled key.
aws s3 cp s3://customer-records-prod/ s3://customer-records-prod/ \
  --recursive --sse aws:kms --sse-kms-key-id $KEY

Full walkthrough (console steps, edge cases and verification) in the lesson Encrypt S3 object storage at rest.

Is S3.17 a false positive?

A bucket that already shows SSE-S3 encryption still fails — the control specifically wants a KMS key, not just "encrypted at rest".

Part of the learning path Encrypt everything
  • S3.1 Account-level S3 public access is not fully blocked
  • S3.2 Public S3 buckets expose data to anyone on the internet
  • S3.3 Buckets can be written to by anyone on the internet
  • S3.5 S3 is accepting unencrypted HTTP requests
  • S3.6 Bucket policy grants broad access to other AWS accounts
  • S3.8 Buckets can still be made public; Block Public Access is off
  • S3.9 No S3 access logs, so reads and writes go unaudited
  • S3.10 Versioned buckets should have lifecycle configurations
  • S3.11 Buckets should have event notifications enabled
  • S3.12 ACLs should not be used to manage bucket access
  • S3.13 Buckets have no lifecycle rules and grow forever
  • S3.15 Buckets should have Object Lock enabled