Skip to main content

DynamoDB Table KMS CMK Encryption

Overview

This check verifies that your DynamoDB tables are encrypted using a customer-managed AWS KMS key (CMK) instead of the default AWS-owned encryption key. While DynamoDB encrypts all data at rest by default, using your own KMS key gives you more control over encryption key management, access policies, and audit capabilities.

Risk

When DynamoDB tables rely on AWS-owned encryption instead of customer-managed KMS keys:

  • Reduced access control: You cannot define custom key policies to restrict who can decrypt your table data
  • Limited auditability: AWS-owned keys do not generate CloudTrail events for encryption operations
  • No independent rotation: You cannot control the key rotation schedule or immediately disable a compromised key
  • Incident response challenges: Without control over the encryption key, you cannot quickly revoke access to data during a security incident
  • Compliance gaps: Many regulatory frameworks require customer-managed encryption keys for sensitive data

Remediation Steps

Prerequisites

  • AWS account access with permissions to modify DynamoDB tables and KMS keys
  • An existing KMS key, or permission to create one
Required IAM permissions

To enable KMS encryption on a DynamoDB table, you need:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:UpdateTable",
"dynamodb:DescribeTable"
],
"Resource": "arn:aws:dynamodb:us-east-1:*:table/*"
},
{
"Effect": "Allow",
"Action": [
"kms:DescribeKey",
"kms:CreateGrant",
"kms:ListGrants"
],
"Resource": "arn:aws:kms:us-east-1:*:key/*"
}
]
}

AWS Console Method

Step 1: Create or identify a KMS key

  1. Open the AWS KMS Console at https://console.aws.amazon.com/kms
  2. Ensure you are in the us-east-1 region (top-right corner)
  3. Click Customer managed keys in the left sidebar
  4. Either select an existing symmetric key, or click Create key:
    • Key type: Symmetric
    • Key usage: Encrypt and decrypt
    • Give it a name like dynamodb-encryption-key
  5. Copy the Key ARN for later use
KMS key policy for DynamoDB

Your KMS key policy must allow DynamoDB to use the key. Add this statement to your key policy:

{
"Sid": "Allow DynamoDB to use the key",
"Effect": "Allow",
"Principal": {
"Service": "dynamodb.amazonaws.com"
},
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey",
"kms:CreateGrant"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:CallerAccount": "<YOUR_ACCOUNT_ID>",
"kms:ViaService": "dynamodb.us-east-1.amazonaws.com"
}
}
}

Replace <YOUR_ACCOUNT_ID> with your 12-digit AWS account ID.

Step 2: Enable KMS encryption on your DynamoDB table

  1. Open the DynamoDB Console at https://console.aws.amazon.com/dynamodb
  2. Ensure you are in the us-east-1 region
  3. Click Tables in the left sidebar
  4. Select the table you want to encrypt
  5. Click on the Additional settings tab
  6. In the Encryption section, click Manage encryption
  7. Select Customer managed key
  8. Choose your KMS key from the dropdown or enter the Key ARN
  9. Click Save changes

The encryption change will take effect immediately. DynamoDB will re-encrypt existing data with your new key in the background.

AWS CLI (optional)

Enable KMS encryption with AWS-managed key

aws dynamodb update-table \
--table-name <YOUR_TABLE_NAME> \
--sse-specification Enabled=true,SSEType=KMS \
--region us-east-1

Enable KMS encryption with customer-managed key

aws dynamodb update-table \
--table-name <YOUR_TABLE_NAME> \
--sse-specification Enabled=true,SSEType=KMS,KMSMasterKeyId=arn:aws:kms:us-east-1:<YOUR_ACCOUNT_ID>:key/<YOUR_KEY_ID> \
--region us-east-1

Replace:

  • <YOUR_TABLE_NAME> with your DynamoDB table name
  • <YOUR_ACCOUNT_ID> with your 12-digit AWS account ID
  • <YOUR_KEY_ID> with your KMS key ID

Check current encryption status

aws dynamodb describe-table \
--table-name <YOUR_TABLE_NAME> \
--region us-east-1 \
--query 'Table.SSEDescription'
CloudFormation (optional)

This template creates a KMS key with proper permissions and a DynamoDB table with customer-managed encryption.

AWSTemplateFormatVersion: '2010-09-09'
Description: DynamoDB table with customer-managed KMS encryption

Parameters:
TableName:
Type: String
Description: Name of the DynamoDB table
Default: my-encrypted-table
HashKeyName:
Type: String
Description: Name of the hash key attribute
Default: id
HashKeyType:
Type: String
Description: Type of the hash key attribute
Default: S
AllowedValues:
- S
- N
- B

Resources:
DynamoDBKMSKey:
Type: AWS::KMS::Key
Properties:
Description: KMS key for DynamoDB table encryption
EnableKeyRotation: true
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: '*'
- Sid: Allow DynamoDB to use the key
Effect: Allow
Principal:
Service: dynamodb.amazonaws.com
Action:
- kms:Encrypt
- kms:Decrypt
- kms:ReEncrypt*
- kms:GenerateDataKey*
- kms:DescribeKey
- kms:CreateGrant
Resource: '*'
Condition:
StringEquals:
kms:CallerAccount: !Ref AWS::AccountId
kms:ViaService: !Sub 'dynamodb.${AWS::Region}.amazonaws.com'

DynamoDBKMSKeyAlias:
Type: AWS::KMS::Alias
Properties:
AliasName: alias/dynamodb-encryption-key
TargetKeyId: !Ref DynamoDBKMSKey

EncryptedDynamoDBTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Ref TableName
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: !Ref HashKeyName
AttributeType: !Ref HashKeyType
KeySchema:
- AttributeName: !Ref HashKeyName
KeyType: HASH
SSESpecification:
SSEEnabled: true
SSEType: KMS
KMSMasterKeyId: !GetAtt DynamoDBKMSKey.Arn

Outputs:
TableArn:
Description: ARN of the encrypted DynamoDB table
Value: !GetAtt EncryptedDynamoDBTable.Arn
KMSKeyArn:
Description: ARN of the KMS key
Value: !GetAtt DynamoDBKMSKey.Arn

Deploy with:

aws cloudformation deploy \
--template-file template.yaml \
--stack-name encrypted-dynamodb-table \
--parameter-overrides TableName=my-secure-table \
--region us-east-1
Terraform (optional)
variable "table_name" {
description = "Name of the DynamoDB table"
type = string
default = "my-encrypted-table"
}

variable "hash_key" {
description = "Name of the hash key attribute"
type = string
default = "id"
}

data "aws_caller_identity" "current" {}
data "aws_region" "current" {}

resource "aws_kms_key" "dynamodb" {
description = "KMS key for DynamoDB table encryption"
deletion_window_in_days = 30
enable_key_rotation = true

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "Enable IAM User Permissions"
Effect = "Allow"
Principal = { AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" }
Action = "kms:*"
Resource = "*"
},
{
Sid = "Allow DynamoDB to use the key"
Effect = "Allow"
Principal = { Service = "dynamodb.amazonaws.com" }
Action = [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey",
"kms:CreateGrant"
]
Resource = "*"
Condition = {
StringEquals = {
"kms:CallerAccount" = data.aws_caller_identity.current.account_id
"kms:ViaService" = "dynamodb.${data.aws_region.current.name}.amazonaws.com"
}
}
}
]
})
}

resource "aws_kms_alias" "dynamodb" {
name = "alias/dynamodb-encryption-key"
target_key_id = aws_kms_key.dynamodb.key_id
}

resource "aws_dynamodb_table" "encrypted" {
name = var.table_name
billing_mode = "PAY_PER_REQUEST"
hash_key = var.hash_key

attribute {
name = var.hash_key
type = "S"
}

server_side_encryption {
enabled = true
kms_key_arn = aws_kms_key.dynamodb.arn
}
}

output "table_arn" {
description = "ARN of the encrypted DynamoDB table"
value = aws_dynamodb_table.encrypted.arn
}

output "kms_key_arn" {
description = "ARN of the KMS key"
value = aws_kms_key.dynamodb.arn
}

Deploy with:

terraform init
terraform plan
terraform apply

Verification

After making changes, verify encryption is enabled:

  1. Open the DynamoDB Console at https://console.aws.amazon.com/dynamodb
  2. Click Tables and select your table
  3. Click the Additional settings tab
  4. In the Encryption section, confirm it shows Customer managed key with your KMS key ARN
Verify with AWS CLI
aws dynamodb describe-table \
--table-name <YOUR_TABLE_NAME> \
--region us-east-1 \
--query 'Table.SSEDescription'

A properly encrypted table will show output like:

{
"Status": "ENABLED",
"SSEType": "KMS",
"KMSMasterKeyArn": "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
}

If the SSEType is AES256 or KMSMasterKeyArn shows alias/aws/dynamodb, the table is not using a customer-managed key.

Additional Resources

Notes

  • No downtime required: Changing encryption settings does not cause table downtime. DynamoDB re-encrypts existing data in the background.
  • Encryption is transparent: Applications do not need code changes. DynamoDB handles encryption and decryption automatically.
  • Key availability: If your KMS key is deleted or disabled, DynamoDB cannot decrypt your table data. Ensure key policies allow necessary access and consider enabling automatic key rotation.
  • Cost considerations: Using customer-managed KMS keys incurs additional charges for key storage and API calls. See AWS KMS Pricing.
  • Global Tables: For DynamoDB Global Tables, each replica can use a different KMS key. Ensure keys exist in each replica region.
  • Symmetric keys only: DynamoDB only supports symmetric KMS keys, not asymmetric keys.
  • Reverting encryption: You can switch back to AWS-owned encryption, but this is generally not recommended for sensitive data.