Skip to main content

S3 Bucket KMS Encryption

Overview

This check verifies that Amazon S3 buckets use server-side encryption with AWS Key Management Service (KMS) keys instead of the default SSE-S3 encryption. KMS encryption provides stronger security controls, including the ability to manage your own encryption keys and audit key usage.

Risk

Without KMS-based encryption:

  • You lose fine-grained access control over who can decrypt your data
  • There is no audit trail of encryption key usage in CloudTrail
  • Cross-account access restrictions are harder to enforce
  • You cannot use customer-managed keys with custom rotation policies

This weakens your overall data protection and makes it harder to detect unauthorized access attempts.

Remediation Steps

Prerequisites

  • AWS Console access with permissions to modify S3 bucket settings
  • The s3:PutEncryptionConfiguration permission on the target bucket
Optional: AWS CLI setup

If you prefer using the command line, ensure the AWS CLI is installed and configured:

aws configure
# Enter your AWS Access Key ID, Secret Access Key, and set region to us-east-1

You will also need kms:DescribeKey permission if using a customer-managed KMS key.

AWS Console Method

  1. Open the Amazon S3 console
  2. Click on the bucket you want to encrypt
  3. Go to the Properties tab
  4. Scroll down to Default encryption and click Edit
  5. Under Encryption type, select Server-side encryption with AWS Key Management Service keys (SSE-KMS)
  6. Choose your key:
    • AWS managed key (aws/s3) - Simplest option, AWS manages the key for you
    • Choose from your AWS KMS keys - Select a customer-managed key you have created
  7. (Recommended) Enable Bucket Key to reduce KMS request costs
  8. Click Save changes
AWS CLI (optional)

Using AWS managed KMS key:

aws s3api put-bucket-encryption \
--bucket <your-bucket-name> \
--server-side-encryption-configuration '{
"Rules": [{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "aws:kms"
},
"BucketKeyEnabled": true
}]
}' \
--region us-east-1

Using a customer-managed KMS key:

aws s3api put-bucket-encryption \
--bucket <your-bucket-name> \
--server-side-encryption-configuration '{
"Rules": [{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "aws:kms",
"KMSMasterKeyID": "arn:aws:kms:us-east-1:123456789012:key/your-key-id"
},
"BucketKeyEnabled": true
}]
}' \
--region us-east-1

Replace <your-bucket-name> with your actual bucket name and update the KMS key ARN as needed.

CloudFormation (optional)
AWSTemplateFormatVersion: '2010-09-09'
Description: S3 bucket with KMS encryption enabled

Parameters:
BucketName:
Type: String
Description: Name of the S3 bucket
KMSKeyArn:
Type: String
Description: ARN of the KMS key for encryption (leave empty for AWS managed key)
Default: ''

Conditions:
UseCustomKey: !Not [!Equals [!Ref KMSKeyArn, '']]

Resources:
EncryptedS3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref BucketName
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: aws:kms
KMSMasterKeyID: !If [UseCustomKey, !Ref KMSKeyArn, !Ref 'AWS::NoValue']
BucketKeyEnabled: true

Outputs:
BucketArn:
Description: ARN of the encrypted S3 bucket
Value: !GetAtt EncryptedS3Bucket.Arn

Deploy the stack:

aws cloudformation deploy \
--template-file template.yaml \
--stack-name s3-kms-encryption \
--parameter-overrides BucketName=<your-bucket-name> \
--region us-east-1
Terraform (optional)
variable "bucket_name" {
description = "Name of the S3 bucket"
type = string
}

variable "kms_key_arn" {
description = "ARN of the KMS key for encryption (optional - uses AWS managed key if not specified)"
type = string
default = null
}

resource "aws_s3_bucket" "encrypted" {
bucket = var.bucket_name
}

resource "aws_s3_bucket_server_side_encryption_configuration" "encrypted" {
bucket = aws_s3_bucket.encrypted.id

rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
kms_master_key_id = var.kms_key_arn
}
bucket_key_enabled = true
}
}

Apply the configuration:

terraform init
terraform apply -var="bucket_name=<your-bucket-name>"

To use a customer-managed key:

terraform apply \
-var="bucket_name=<your-bucket-name>" \
-var="kms_key_arn=arn:aws:kms:us-east-1:123456789012:key/your-key-id"

Verification

After making changes, verify the encryption is properly configured:

  1. In the S3 console, go to your bucket's Properties tab
  2. Under Default encryption, confirm it shows Server-side encryption with AWS Key Management Service keys (SSE-KMS)
  3. Verify Bucket Key is enabled (recommended for cost optimization)
CLI verification
aws s3api get-bucket-encryption \
--bucket <your-bucket-name> \
--region us-east-1

Expected output should show SSEAlgorithm as aws:kms:

{
"ServerSideEncryptionConfiguration": {
"Rules": [
{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "aws:kms",
"KMSMasterKeyID": "arn:aws:kms:us-east-1:..."
},
"BucketKeyEnabled": true
}
]
}
}

Additional Resources

Notes

  • Existing objects are not re-encrypted. Changing the default encryption only affects new objects uploaded after the change. To encrypt existing objects, you must copy them in place or use S3 Batch Operations.
  • S3 Bucket Keys reduce KMS API costs by caching the data key at the bucket level. This is recommended for most use cases.
  • Customer-managed keys give you full control over key policies, rotation, and deletion. Use them when you need to meet specific compliance requirements.
  • Cross-account access requires additional KMS key policy configuration if the bucket and KMS key are in different accounts.
  • For highly sensitive data, consider DSSE-KMS (dual-layer server-side encryption) which applies two layers of encryption.