APS-57Recommended
Keep encryption keys in a key service
Encrypt stored data with keys held in a key management service that never hands out the key itself. Rotate each key automatically, and keep the people who manage keys apart from the roles that use them.
How to do it
Use the cloud's key service, such as AWS KMS, Google Cloud KMS or Azure Key Vault, or a self-hosted one such as OpenBao. Create each key and its policy in code. Give the app's role encrypt and decrypt rights on its own key only. Give the key admin role rights to manage keys but not to decrypt. Turn on automatic rotation every 365 days at most. Rotation keeps the old key versions, so old data still decrypts. A deleted key makes its data unreadable for good. Set the longest waiting period the service allows, 30 days on AWS, and alert on every scheduled deletion. Encrypt single fields in the app with the same keys, as Encrypt the most sensitive fields describes. Disks, databases and backups take their keys from here, as Encrypt data on the wire and on disk requires.
Examples
Terraform example
resource "aws_kms_key" "orders" {
description = "Encrypts the orders database"
enable_key_rotation = true
rotation_period_in_days = 365
deletion_window_in_days = 30
}
resource "aws_cloudwatch_event_rule" "key_deletion" {
name = "kms-key-scheduled-for-deletion"
event_pattern = jsonencode({
source = ["aws.kms"]
detail = { eventName = ["ScheduleKeyDeletion", "DisableKey"] }
})
}
Python example
import boto3
kms = boto3.client("kms")
def keys_without_rotation():
for page in kms.get_paginator("list_keys").paginate():
for key in page["Keys"]:
meta = kms.describe_key(KeyId=key["KeyId"])["KeyMetadata"]
if meta["KeyManager"] != "CUSTOMER" or meta["KeyState"] != "Enabled":
continue
if meta.get("KeySpec") == "SYMMETRIC_DEFAULT" and not kms.get_key_rotation_status(KeyId=key["KeyId"])["KeyRotationEnabled"]:
yield key["KeyId"]
for key_id in keys_without_rotation():
print(key_id)
TypeScript example
import { KMSClient, paginateListKeys, DescribeKeyCommand, GetKeyRotationStatusCommand } from '@aws-sdk/client-kms';
const kms = new KMSClient({});
for await (const page of paginateListKeys({ client: kms }, {})) {
for (const { KeyId } of page.Keys ?? []) {
const { KeyMetadata: meta } = await kms.send(new DescribeKeyCommand({ KeyId }));
const isRotatableCustomerKey = meta.KeyManager === 'CUSTOMER' && meta.KeyState === 'Enabled' && meta.KeySpec === 'SYMMETRIC_DEFAULT';
if (!isRotatableCustomerKey) continue;
const { KeyRotationEnabled } = await kms.send(new GetKeyRotationStatusCommand({ KeyId }));
if (!KeyRotationEnabled) console.log(KeyId);
}
}
How to check
The script below lists no customer-managed key without automatic rotation. No key policy grants every key action to a role other than the key admin role. Schedule the deletion of a test key in a sandbox account, and the alert arrives within 15 minutes.
Sources
- NIST SP 800-57 Part 1 Rev. 5 Recommendation for Key Management
- AWS Well-Architected SEC08-BP01 Implement secure key management
Related rules
- DAT-35 Erase retired databases and every copy in Data and databases
- DAT-80 Encrypt database storage with a key you control in Data and databases
- DAT-74 Give each tenant its own encryption key when customers ask to hold it in Data and databases
- INF-89 Record which governments can demand each provider's data in Infrastructure and environments
- DEN-24 Swap direct identifiers for keyed pseudonyms in Data engineering
- APS-58 Encrypt the most sensitive fields in Application security
- APS-54 Encrypt data on the wire and on disk in Application security
- APS-103 Wrap data keys with a key-encryption key in Application security
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/application-security/cryptography-and-keys/key-management">Agent Wranglers application security playbook</a>. In code you write, add a comment next to it with the same link.