This check flags SNS topics without server-side encryption (SSE). Messages sitting in an unencrypted topic are stored as plaintext at rest, which can fail compliance audits and expose sensitive payloads. Fix it by attaching a KMS key with set-topic-attributes and setting KmsMasterKeyId.
Amazon SNS sits in the middle of a lot of event-driven plumbing. Order notifications, security alerts, fan-out to SQS and Lambda, mobile push, billing events. Plenty of those messages carry data you would not want sitting around in plaintext: email addresses, internal hostnames, account IDs, sometimes full record payloads. The SNS Topic Not Encrypted check looks for topics that have no server-side encryption configured, which means the data they hold at rest is not protected by a KMS key.
What this check detects
The check inspects every SNS topic in your AWS account and reports any topic where the KmsMasterKeyId attribute is empty. When that attribute is unset, SNS does not encrypt message data at rest. Once you assign a KMS key, SNS encrypts messages as they arrive and decrypts them on delivery, transparently to publishers and subscribers.
Note: SNS server-side encryption protects data at rest inside the SNS service. Traffic between publishers, SNS, and subscribers already moves over TLS, so this is about the storage layer, not the wire. Both matter, but they solve different problems.
You can confirm the current state of a topic from the CLI:
aws sns get-topic-attributes \
--topic-arn arn:aws:sns:us-east-1:111122223333:orders-events \
--query 'Attributes.KmsMasterKeyId' \
--output text
If that returns None, the topic is unencrypted and this check will fail for it.
Why it matters
Encryption at rest is one of those controls that feels invisible until an auditor or an incident makes it visible. Here is where an unencrypted SNS topic actually bites you:
- Compliance gaps. PCI DSS, HIPAA, SOC 2, and most internal data-handling policies expect sensitive data to be encrypted at rest with managed keys. An unencrypted topic is a finding that shows up in every audit cycle until it is fixed.
- Blast radius during a breach. If an attacker gains broad read access to your account, an encrypted topic adds a second hurdle. They also need
kms:Decrypton the key. Without encryption, message data is readable the moment they reach the SNS API. - No key-level access control. KMS gives you an extra IAM surface. You can scope who is allowed to publish to or subscribe from a topic at the key-policy level, which is harder to bypass than topic policy alone.
- Defense in depth. SNS topics often fan out to SQS queues and Lambda functions that you have carefully encrypted. Leaving the topic itself in plaintext breaks the chain.
A common real-world scenario: a security tool publishes GuardDuty or CloudTrail-derived alerts to an SNS topic. Those alerts describe your infrastructure, sometimes including resource ARNs, IP addresses, and the nature of detected activity. An attacker who can read that topic gets a free map of what your defenses know about them. Encrypting it raises the bar.
How to fix it
You enable SSE by assigning a KMS key to the topic. You can use the AWS-managed key alias/aws/sns for a quick win, or a customer-managed key (CMK) for tighter control and your own rotation policy.
Option 1: AWS CLI
Encrypt an existing topic with the AWS-managed SNS key:
aws sns set-topic-attributes \
--topic-arn arn:aws:sns:us-east-1:111122223333:orders-events \
--attribute-name KmsMasterKeyId \
--attribute-value alias/aws/sns
Or point it at a customer-managed key:
aws sns set-topic-attributes \
--topic-arn arn:aws:sns:us-east-1:111122223333:orders-events \
--attribute-name KmsMasterKeyId \
--attribute-value alias/sns-prod-cmk
Warning: If you use a customer-managed key, every service that publishes to the topic needs kms:GenerateDataKey and kms:Decrypt on that key, and every cross-service subscriber path needs decrypt permission too. A missing key-policy grant shows up as silently dropped or failed deliveries, not an obvious error. Update the key policy before you flip encryption on for high-traffic topics.
If CloudWatch Alarms, S3 event notifications, or other AWS services publish to the topic, grant the service principal access in the KMS key policy:
{
"Sid": "AllowAWSServicesToPublish",
"Effect": "Allow",
"Principal": {
"Service": [
"cloudwatch.amazonaws.com",
"events.amazonaws.com",
"s3.amazonaws.com"
]
},
"Action": [
"kms:GenerateDataKey*",
"kms:Decrypt"
],
"Resource": "*"
}
Option 2: AWS Console
- Open the Amazon SNS console and select your topic.
- Click Edit.
- Expand the Encryption section.
- Toggle Encryption on.
- Choose a KMS key (the default
alias/aws/snsor a CMK from the dropdown). - Click Save changes.
Option 3: Terraform
Set kms_master_key_id on the topic resource so the encrypted state is your source of truth:
resource "aws_kms_key" "sns" {
description = "CMK for SNS topic encryption"
enable_key_rotation = true
deletion_window_in_days = 30
}
resource "aws_sns_topic" "orders_events" {
name = "orders-events"
kms_master_key_id = aws_kms_key.sns.id
}
Option 4: CloudFormation
Resources:
OrdersEventsTopic:
Type: AWS::SNS::Topic
Properties:
TopicName: orders-events
KmsMasterKeyId: alias/aws/sns
Tip: Enabling encryption on an existing topic is a non-destructive metadata change. It does not recreate the topic, does not drop subscriptions, and does not affect in-flight messages. The only real risk is the KMS permission gap above, so verify key policy first and the change itself is safe to apply live.
How to prevent it from happening again
Fixing one topic is easy. Keeping every future topic encrypted is the real goal. Push the control left so an unencrypted topic never reaches production.
Service Control Policy to deny unencrypted creation
Block any attempt to create an SNS topic without a KMS key across the org:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyUnencryptedSNS",
"Effect": "Deny",
"Action": "sns:CreateTopic",
"Resource": "*",
"Condition": {
"Null": {
"sns:KmsMasterKeyId": "true"
}
}
}
]
}
Catch it in CI with policy-as-code
If you use Terraform, an OPA/Conftest rule fails the plan before it merges:
package terraform.sns
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_sns_topic"
not resource.change.after.kms_master_key_id
msg := sprintf("SNS topic '%s' must set kms_master_key_id", [resource.address])
}
Wire that into your pipeline so the gate runs on every pull request:
terraform plan -out tfplan.binary
terraform show -json tfplan.binary > tfplan.json
conftest test tfplan.json --policy policy/
Tip: Lensix runs the sns_unencrypted check continuously, so even topics created outside your IaC pipeline (console clicks, scripts, third-party tools) get caught. Pair the SCP for prevention with continuous scanning for the drift the SCP cannot see.
Best practices
- Default to a customer-managed key for sensitive topics. AWS-managed keys are fine for low-risk notifications, but a CMK gives you control over key policy, rotation, and the ability to revoke access fast during an incident.
- Turn on automatic key rotation. Set
enable_key_rotation = trueon your CMKs so they rotate yearly without manual effort. - Encrypt the whole pipeline. An encrypted SNS topic feeding an unencrypted SQS queue still leaks at the next hop. Apply the same standard to the SQS queues, Lambda environment variables, and any S3 buckets in the chain.
- Scope key policies tightly. Grant only the specific service principals and roles that publish or subscribe. Avoid
"Principal": "*"on KMS key policies. - Use a dedicated key per environment or data domain. Separate prod and non-prod keys so a compromise in one environment does not unlock the other.
- Watch deliveries after enabling encryption. Monitor the
NumberOfNotificationsFailedCloudWatch metric for a short window after changing the key. A spike usually means a subscriber is missing decrypt permission.
Server-side encryption on SNS is one of the cheapest security controls you can apply. There is no extra SNS charge for using it, the only cost is normal KMS request pricing, and the change is non-disruptive when you handle key permissions first. Get every topic encrypted, gate new topics in CI, and let continuous scanning catch the rest.

