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:PutEncryptionConfigurationpermission 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
- Open the Amazon S3 console
- Click on the bucket you want to encrypt
- Go to the Properties tab
- Scroll down to Default encryption and click Edit
- Under Encryption type, select Server-side encryption with AWS Key Management Service keys (SSE-KMS)
- 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
- (Recommended) Enable Bucket Key to reduce KMS request costs
- 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:
- In the S3 console, go to your bucket's Properties tab
- Under Default encryption, confirm it shows Server-side encryption with AWS Key Management Service keys (SSE-KMS)
- 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
- Amazon S3 Default Bucket Encryption
- Protecting Data Using Server-Side Encryption with KMS
- Reducing the Cost of SSE-KMS with S3 Bucket Keys
- AWS KMS Key Management
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.