Amazon S3 default encryption sets encryption settings for all object uploads, but these settings are not enforced. This may cause unencrypted objects to be uploaded to the bucket. This blog gives you a bucket policy that enforces all object uploads to be encrypted.
The following CloudFormation template enforces the use of KMS encryption with a bucket policy.
Resources:
Bucket:
Type: AWS::S3::Bucket
Properties:
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: aws:kms
KMSMasterKeyID: !GetAtt EncryptionKey.Arn
BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref Bucket
PolicyDocument:
Statement:
- Sid: Restrict to Default- or KMS-encryption
Effect: Deny
Principal: '*'
Action: 's3:PutObject'
Resource: !Sub
- '${BucketArn}/*'
- BucketArn: !GetAtt Bucket.Arn
Condition:
'Null':
s3:x-amz-server-side-encryption: false
StringNotEquals:
s3:x-amz-server-side-encryption: 'aws:kms'
- Sid: Restrict KMS-key
Effect: Deny
Principal: '*'
Action: 's3:PutObject'
Resource: !Sub
- '${BucketArn}/*'
- BucketArn: !GetAtt Bucket.Arn
Condition:
StringNotEquals:
s3:x-amz-server-side-encryption: 'aws:kms'
StringNotEqualsIfExists:
s3:x-amz-server-side-encryption-aws-kms-key-id: !GetAtt EncryptionKey.Arn
EncryptionKey:
Type: AWS::KMS::Key
Properties:
KeyPolicy:
Version: '2012-10-17'
Statement:
- Sid: Enable IAM User Permissions
Effect: Allow
Principal:
AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
Action: 'kms:*'
Resource: '*'
Lines 7 to 9 enable default encryption to automatically encrypt all objects uploaded without any encryption information. The default encryption is set to algorithm KMS and key EncryptionKey.
- ServerSideEncryptionByDefault:
SSEAlgorithm: aws:kms
KMSMasterKeyID: !GetAtt EncryptionKey.Arn
The bucket policy enforces the default encryption settings by denying uploads with invalid encryption information. To enforce KMS-encryption we deny uploads with invalid encryption algorithms using the s3:x-amz-server-side-encryption-condition key. To enforce KMS-key EncryptionKey we deny uploads with an invalid encryption key using the s3:x-amz-server-side-encryption-aws-kms-key-id-condition key.
Enforcing KMS-encryption
- Sid: Restrict to Default- or KMS-encryption
Effect: Deny
Principal: '*'
Action: 's3:PutObject'
Resource: !Sub
- '${BucketArn}/*'
- BucketArn: !GetAtt Bucket.Arn
Condition:
'Null':
s3:x-amz-server-side-encryption: false
StringNotEquals:
s3:x-amz-server-side-encryption: 'aws:kms'
The Null-condition allows uploads without encryption information and the StringNotEquals-condition denies uploads with invalid encryption information. Note that using StringNotEqualsIfExists doesn’t work for uploads without encryption information. The condition evaluates to true and denies the upload because of the Deny-effect.
Enforcing KMS-key
- Sid: Restrict KMS-key
Effect: Deny
Principal: '*'
Action: 's3:PutObject'
Resource: !Sub
- '${BucketArn}/*'
- BucketArn: !GetAtt Bucket.Arn
Condition:
StringNotEquals:
s3:x-amz-server-side-encryption: 'aws:kms'
StringNotEqualsIfExists:
s3:x-amz-server-side-encryption-aws-kms-key-id: !GetAtt EncryptionKey.Arn
The StringNotEquals-condition scopes the policy to KMS-encrypted uploads. Additionally the StringNotEqualsIfExists-condition denies uploads with invalid encryption keys. Note that checking the encryption key is insufficient. S3 uses the AWS managed CMK when the algorithm is set, but the key isn’t.
If you specify x-amz-server-side-encryption:aws:kms, but don’t provide x-amz-server-side-encryption-aws-kms-key-id, Amazon S3 uses the AWS managed CMK in AWS KMS to protect the data.
Source: PutObject API Reference