This check flags SNS topics encrypted with an AWS-managed KMS key (aws/sns) instead of a customer-managed key (CMK). Without a CMK you lose control over key rotation, granular access policies, and audit visibility. Fix it by creating a CMK and updating the topic's KmsMasterKeyId attribute.
Server-side encryption for Amazon SNS protects the messages sitting in a topic before they fan out to subscribers. When you enable it, every published message is encrypted at rest using AWS KMS. The catch is that there are two ways to do this, and they are not equal from a security or compliance standpoint.
You can encrypt with the default AWS-managed key (aws/sns), or you can supply your own customer-managed key (CMK). This check, sns_nocmk, fires when a topic is using the AWS-managed key. It is technically encrypted, but you have given up the controls that make encryption meaningful in a security audit.
What this check detects
Lensix inspects every SNS topic in your account and reads its KmsMasterKeyId attribute. The check evaluates three possible states:
- No encryption — the attribute is absent. Messages are not encrypted at rest at all.
- AWS-managed key — the attribute is set to
alias/aws/sns. This is whatsns_nocmkflags. - Customer-managed key — the attribute points to a CMK you created. This passes.
Note: The AWS-managed key (aws/sns) is created automatically the first time you enable encryption with the default option. You cannot edit its key policy, you cannot disable it, and AWS controls its rotation schedule. It is shared across all SNS topics in your account that use the default.
A common point of confusion: a topic using aws/sns is encrypted. This check is not about a lack of encryption. It is about who controls the key and what you can prove about it.
Why it matters
The difference between an AWS-managed key and a CMK comes down to control, and control is exactly what auditors and incident responders care about.
You cannot restrict who decrypts your messages
With a CMK, the key policy is yours. You can write conditions that limit decryption to specific roles, specific source services, or specific VPC endpoints. With the AWS-managed key, there is no key policy you can touch. Any principal in your account with the right SNS and KMS permissions can interact with topics encrypted by it, and you cannot add a second layer of key-level restriction.
Key rotation is on AWS's schedule, not yours
AWS rotates the aws/sns key automatically, but you do not control the cadence and you cannot force an emergency rotation. If your compliance framework requires annual rotation with evidence, or requires the ability to rotate after a suspected compromise, the managed key cannot satisfy that on demand. A CMK lets you enable automatic rotation and trigger manual rotation when you need it.
Audit and revocation become hard
CloudTrail logs KMS decrypt calls against a CMK with the key ARN you recognize, which makes tracing "who decrypted what and when" far easier during an investigation. More importantly, if a key is involved in an incident, you can disable or schedule deletion of a CMK to instantly cut off access. You cannot disable the AWS-managed key.
Warning: Many compliance baselines, including parts of PCI DSS, HIPAA, and FedRAMP, expect customer control over encryption keys for sensitive data flows. SNS topics carrying PII, order events, or security alerts often fall into this category. The AWS-managed key will not pass these reviews even though the data is encrypted.
Cross-account fan-out gets safer
If your SNS topic delivers to subscribers in other accounts, a CMK lets you grant those accounts decrypt permission explicitly through the key policy. With the managed key, cross-account encrypted delivery does not work cleanly because you cannot share the key. Teams often disable encryption entirely to work around this, which is the worst outcome.
How to fix it
The fix is two steps: create a CMK with an appropriate key policy, then point the SNS topic at it.
Step 1: Create a customer-managed key
aws kms create-key \
--description "CMK for SNS topic encryption" \
--key-usage ENCRYPT_DECRYPT \
--tags TagKey=Purpose,TagValue=sns-encryption
Note the KeyId in the output. Then create a friendly alias so you do not have to track raw key IDs:
aws kms create-alias \
--alias-name alias/sns-cmk \
--target-key-id <key-id-from-previous-step>
Step 2: Attach a key policy that lets SNS use the key
SNS needs permission to generate data keys and decrypt with your CMK. Save this as key-policy.json and adjust the account ID and service principals to match your delivery targets (for example, add the principals of services that publish to or subscribe to the topic).
{
"Version": "2012-10-17",
"Id": "sns-cmk-policy",
"Statement": [
{
"Sid": "AllowAccountAdmin",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::123456789012:root" },
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "AllowSNSService",
"Effect": "Allow",
"Principal": { "Service": "sns.amazonaws.com" },
"Action": [
"kms:Decrypt",
"kms:GenerateDataKey*"
],
"Resource": "*"
},
{
"Sid": "AllowPublishersToUseKey",
"Effect": "Allow",
"Principal": { "Service": "cloudwatch.amazonaws.com" },
"Action": [
"kms:Decrypt",
"kms:GenerateDataKey*"
],
"Resource": "*"
}
]
}
aws kms put-key-policy \
--key-id <key-id> \
--policy-name default \
--policy file://key-policy.json
Warning: If a service that publishes to the topic (CloudWatch Alarms, S3 event notifications, Lambda, etc.) does not have kms:GenerateDataKey* on the CMK, publishing will silently fail with a KMS access denied error and your notifications stop arriving. Map every publisher and subscriber before you switch keys.
Step 3: Enable automatic rotation
aws kms enable-key-rotation --key-id <key-id>
Step 4: Point the SNS topic at the CMK
aws sns set-topic-attributes \
--topic-arn arn:aws:sns:us-east-1:123456789012:my-topic \
--attribute-name KmsMasterKeyId \
--attribute-value alias/sns-cmk
Verify the change:
aws sns get-topic-attributes \
--topic-arn arn:aws:sns:us-east-1:123456789012:my-topic \
--query "Attributes.KmsMasterKeyId"
Note: Switching the key affects messages encrypted from that point forward. There is no re-encryption of in-flight messages, and SNS does not retain delivered messages, so the cutover is clean. Just confirm publishers and subscribers keep working immediately after.
Terraform example
If you manage infrastructure as code, define the key and topic together so the relationship is explicit and never drifts:
resource "aws_kms_key" "sns" {
description = "CMK for SNS topic encryption"
enable_key_rotation = true
deletion_window_in_days = 30
}
resource "aws_kms_alias" "sns" {
name = "alias/sns-cmk"
target_key_id = aws_kms_key.sns.key_id
}
resource "aws_sns_topic" "main" {
name = "my-topic"
kms_master_key_id = aws_kms_key.sns.key_id
}
Tip: Reuse a single, well-scoped SNS CMK across topics that share the same trust boundary rather than creating one key per topic. Fewer keys means simpler policies and lower KMS cost, while still giving you the control benefits over the AWS-managed default.
How to prevent it from happening again
Fixing one topic is easy. Stopping the next engineer from spinning up a topic with the default key is the real win.
Gate it in CI/CD with policy-as-code
If you use Terraform, add a Checkov or OPA rule that fails the pipeline when an aws_sns_topic has no kms_master_key_id or uses the AWS-managed alias. A minimal OPA/Rego policy for a Terraform plan looks like this:
package sns
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_sns_topic"
key := resource.change.after.kms_master_key_id
not startswith(key, "arn:aws:kms")
not startswith(key, "alias/")
msg := sprintf("SNS topic %s must use a customer-managed KMS key", [resource.address])
}
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_sns_topic"
resource.change.after.kms_master_key_id == "alias/aws/sns"
msg := sprintf("SNS topic %s uses the AWS-managed key, not a CMK", [resource.address])
}
Enforce with Service Control Policies
At the organization level, you can require that any sns:CreateTopic or sns:SetTopicAttributes call specifying a key references a CMK, denying the managed alias. This is stricter and harder to bypass than a pipeline check.
Continuous detection with Lensix
Pipeline gates only catch resources created through the pipeline. Topics created by hand in the console, by other tools, or by AWS services on your behalf slip past. Lensix runs the sns_nocmk check continuously across every region and account, so a topic that drifts to the AWS-managed key shows up in your findings instead of waiting for an audit.
Best practices
- Always encrypt SNS topics carrying sensitive data with a CMK, not the managed default, especially anything with PII, financial events, or security signals.
- Enable automatic key rotation on every CMK and document the cadence so it maps to your compliance requirements.
- Scope key policies tightly. Grant
kms:Decryptandkms:GenerateDataKey*only to the SNS service and the specific publishers and subscribers that need it. - Tag your keys with purpose and owner so an investigator can immediately tell what a CMK protects.
- Audit decrypt activity in CloudTrail and alert on unexpected principals calling decrypt against your SNS CMK.
- Use a deletion window of at least 30 days on the CMK so a mistaken deletion is recoverable before the key is gone.
Danger: Never schedule deletion of a CMK that is actively protecting a production SNS topic without first repointing the topic to a replacement key and confirming delivery. Once the deletion window passes, the key is gone permanently and every message encrypted under it becomes unrecoverable.
Encryption at rest is a baseline, but the value of encryption is in the controls around the key. Moving SNS topics from the AWS-managed key to a customer-managed key turns "the data is encrypted" into "we control, rotate, audit, and can revoke access to the key that encrypts the data." That distinction is what gets you through an audit and what limits the blast radius when something goes wrong.

