Résumé
ONYEDIKACHI BERNARDCLOUD · DEVSECOPS · AI
RésuméGet in touch
CASE 13STORAGE & DATA

S3 SSE-KMS Encryption and Validation

A bucket relied on S3-managed encryption, so S3 read access alone was enough to read its data. I switched new writes to SSE-KMS with a customer-managed key and proved a role without kms:Decrypt now gets AccessDenied.

Status: CONFIGURED · VERIFIED

DOMAIN
Storage & data
PLATFORM
Amazon S3, AWS KMS
SEVERITY
Medium
OUTCOME
KMS boundary added

TOOLS

AWS CLIAmazon S3AWS KMSAWS IAMAWS STSS3 server-side encryptionSSE-S3SSE-KMSS3 Bucket Keys
RESULT
BEFORES3 read was enough
AFTERkms:Decrypt also required
01

Objective

I set out to find how the bucket's data was encrypted at rest, whether objects used S3-managed or customer-managed keys, configure SSE-KMS with a customer-managed key, and prove it adds a second authorization check.

02

Scenario

The bucket used the default SSE-S3 encryption. That encrypts data at rest, but it adds no separate key permission: anyone with S3 read access can read the objects. An existing customer-managed key, alias/lab-cmk, was available to use instead.

03

What I found

bucket-and-object-encryptionSSE-S3
Bucket SSEAlgorithm:  AES256
Object SSE:           AES256
Object SSEKMSKeyId:   null
FINDING 01MEDIUM

No separate key boundary

The bucket and an existing object both used S3-managed encryption.

SSE-S3 is real encryption at rest, but access to the data depends only on S3 permissions. There was no customer-managed key that could add its own authorization step.

04

What I did

  1. Read the bucket encryption

    It used AES256 (SSE-S3).

  2. Inspected an existing object

    head-object showed AES256 with no KMS key.

  3. Found the customer-managed key

    I located alias/lab-cmk and retrieved its key ARN.

  4. Switched the bucket to SSE-KMS

    I set the default encryption to aws:kms with that key and turned on S3 Bucket Keys to cut KMS request volume.

  5. Tested the boundary

    I uploaded a new object, confirmed it was KMS-encrypted, then assumed a role with S3 read access but no kms:Decrypt and tried to read it.

EVIDENCE

BASH
aws s3api put-bucket-encryption --bucket "$BUCKET_NAME" \
  --server-side-encryption-configuration '{
    "Rules": [{
      "ApplyServerSideEncryptionByDefault": { "SSEAlgorithm": "aws:kms", "KMSMasterKeyID": "'$KEY_ARN'" },
      "BucketKeyEnabled": true
    }]
  }'
05

Validation

The bucket now encrypts new writes with the customer-managed key, and S3 read access alone is no longer enough:

TEXT
Bucket:        SSEAlgorithm aws:kms, key alias/lab-cmk, BucketKeyEnabled true
New object:    SSE aws:kms
Old object:    SSE AES256 (unchanged)
Role without kms:Decrypt:   AccessDenied, not authorized to perform kms:Decrypt

Changing the default did not re-encrypt the object that was already there. That has to be done separately.

06

Before and after

CONFIGURATIONBEFOREAFTER
Bucket encryptionSSE-S3SSE-KMS
Key managementS3-managedCustomer-managed alias/lab-cmk
Bucket KeyNot configuredEnabled
Existing objectAES256Still AES256
New objectNot applicableaws:kms
S3 read aloneEnoughNot enough
kms:DecryptNot neededRequired
07

What I recommend

  1. 01Use SSE-KMS where you need control over the key and a second authorization check.
  2. 02Keep both S3 and KMS permissions to least privilege, and guard kms:Decrypt closely.
  3. 03Turn on S3 Bucket Keys to reduce KMS requests and cost.
  4. 04Re-encrypt existing sensitive objects separately; changing the default does not touch them.
  5. 05Monitor key usage and permissions.

“Changing a bucket's default encryption protects new writes but leaves existing objects as they were, and SSE-KMS adds a kms:Decrypt check on top of ordinary S3 read access.”

WHAT I LEARNED

SKILLS IT PROVES

  • Cloud Security
  • Amazon S3 Security
  • AWS KMS
  • Encryption at Rest
  • Key Management
  • Access Control
  • Least Privilege
  • Security Validation
  • AWS CLI
View my labs on GitHub