Skip to content

SNS topics should be encrypted at rest

SNS topics often carry sensitive payloads: PII in transactional notifications, secrets in deployment pipelines, or financial data in event-driven architectures. Without KMS encryption at rest, message content stored by SNS (including retained messages for retries and fanout) stays unencrypted in the service's internal storage. A misconfigured IAM policy or compromised credential could expose it.

Adding KMS encryption creates an authorization boundary. Even if an attacker gains read access to the underlying storage, they still need kms:Decrypt permission on the specific key. That separation of access is one of the most cost-effective data protection controls you can apply, at roughly $1/month per KMS key plus $0.03 per 10,000 requests.

Retrofit consideration

Enabling encryption on an existing SNS topic doesn't require recreation and won't disrupt the topic itself. The catch is permissions: publishing identities and the SNS service both need kms:GenerateDataKey and kms:Decrypt on the key before you apply. Miss this and the next publish attempt throws AccessDeniedException.

Implementation

Choose the approach that matches how you manage Terraform.

Use the compliance.tf module to enforce this control by default. See get started with compliance.tf.

module "sns" {
  source  = "soc2.compliance.tf/terraform-aws-modules/sns/aws"
  version = ">=7.0.0"

  name = "abc123"
}

module "sns" {
  source  = "pcidss.compliance.tf/terraform-aws-modules/sns/aws"
  version = ">=7.0.0"

  name = "abc123"
}

module "sns" {
  source  = "hipaa.compliance.tf/terraform-aws-modules/sns/aws"
  version = ">=7.0.0"

  name = "abc123"
}

module "sns" {
  source  = "gdpr.compliance.tf/terraform-aws-modules/sns/aws"
  version = ">=7.0.0"

  name = "abc123"
}

module "sns" {
  source  = "nist80053.compliance.tf/terraform-aws-modules/sns/aws"
  version = ">=7.0.0"

  name = "abc123"
}

module "sns" {
  source  = "nistcsf.compliance.tf/terraform-aws-modules/sns/aws"
  version = ">=7.0.0"

  name = "abc123"
}

module "sns" {
  source  = "fedrampmoderate.compliance.tf/terraform-aws-modules/sns/aws"
  version = ">=7.0.0"

  name = "abc123"
}

module "sns" {
  source  = "nist800171.compliance.tf/terraform-aws-modules/sns/aws"
  version = ">=7.0.0"

  name = "abc123"
}

module "sns" {
  source  = "awswellarchitected.compliance.tf/terraform-aws-modules/sns/aws"
  version = ">=7.0.0"

  name = "abc123"
}

module "sns" {
  source  = "cisacyberessentials.compliance.tf/terraform-aws-modules/sns/aws"
  version = ">=7.0.0"

  name = "abc123"
}

module "sns" {
  source  = "nydfs23.compliance.tf/terraform-aws-modules/sns/aws"
  version = ">=7.0.0"

  name = "abc123"
}

module "sns" {
  source  = "cccsmedium.compliance.tf/terraform-aws-modules/sns/aws"
  version = ">=7.0.0"

  name = "abc123"
}

module "sns" {
  source  = "acscism2023.compliance.tf/terraform-aws-modules/sns/aws"
  version = ">=7.0.0"

  name = "abc123"
}

module "sns" {
  source  = "eugmpannex11.compliance.tf/terraform-aws-modules/sns/aws"
  version = ">=7.0.0"

  name = "abc123"
}

module "sns" {
  source  = "cfrpart11.compliance.tf/terraform-aws-modules/sns/aws"
  version = ">=7.0.0"

  name = "abc123"
}

module "sns" {
  source  = "rbicybersecurity.compliance.tf/terraform-aws-modules/sns/aws"
  version = ">=7.0.0"

  name = "abc123"
}

module "sns" {
  source  = "fedramplow.compliance.tf/terraform-aws-modules/sns/aws"
  version = ">=7.0.0"

  name = "abc123"
}

module "sns" {
  source  = "hipaasecurity2003.compliance.tf/terraform-aws-modules/sns/aws"
  version = ">=7.0.0"

  name = "abc123"
}

module "sns" {
  source  = "iso270012013.compliance.tf/terraform-aws-modules/sns/aws"
  version = ">=7.0.0"

  name = "abc123"
}

module "sns" {
  source  = "nist80053rev4.compliance.tf/terraform-aws-modules/sns/aws"
  version = ">=7.0.0"

  name = "abc123"
}

module "sns" {
  source  = "pcidssv321.compliance.tf/terraform-aws-modules/sns/aws"
  version = ">=7.0.0"

  name = "abc123"
}

If you use terraform-aws-modules/sns/aws, set the right module inputs for this control. You can later migrate to the compliance.tf module with minimal changes because it is compatible by design.

module "sns" {
  source  = "terraform-aws-modules/sns/aws"
  version = ">=7.0.0"

  name = "abc123"
}

Use AWS provider resources directly. See docs for the resources involved: aws_sns_topic.

resource "aws_sns_topic" "this" {
  kms_master_key_id = "alias/aws/sns"
  name              = "pofix-abc123"
}

What this control checks

The control checks that the aws_sns_topic resource has kms_master_key_id set to a non-empty value. That value can be a key ID, key ARN, or alias pointing to either a customer-managed KMS key or the AWS-managed SNS key. It fails when the argument is omitted or empty.

Using a customer-managed key is recommended for key rotation control and cross-account access policies. With that approach, the aws_kms_key key policy must grant SNS service access plus any publisher and subscriber permissions your integration requires.

Common pitfalls

  • Subscribers lack KMS permissions

    Adding kms_master_key_id changes the access path for every producer and consumer, not just the topic itself. The SNS service needs kms:GenerateDataKey and kms:Decrypt on the key. SQS subscriptions are the most common failure point: both the queue policy and the KMS key policy must allow the access path, and a gap in either causes delivery failures. Test end-to-end with a subscriber before rolling this out to production topics.

  • AWS-managed key blocks cross-account access

    Using alias/aws/sns as the kms_master_key_id works for single-account setups but prevents cross-account subscriptions because the AWS-managed key policy cannot be modified. If any subscriber lives in a different AWS account, use a customer-managed aws_kms_key with explicit cross-account grants.

  • Terraform import loses encryption setting

    When importing an existing encrypted SNS topic with terraform import, the kms_master_key_id attribute may not appear in state if the Terraform configuration block omits it. The next terraform apply would then remove encryption. Always declare kms_master_key_id in the configuration before importing.

  • FIFO topic ordering unaffected but throughput may change

    KMS encryption on FIFO SNS topics adds per-message latency from KMS calls. High-throughput FIFO topics can hit KMS request rate limits, which are regional and account-specific. Check AWS Service Quotas before enabling encryption on any topic publishing at scale and request increases proactively.

Audit evidence

Config rule evaluation results are the primary artifact: all in-scope topics should show COMPLIANT against SNS_ENCRYPTED_KMS or an equivalent custom rule. Console screenshots of each topic's Encryption section showing a KMS key ARN work as supplementary evidence. CloudTrail SetTopicAttributes events with KmsMasterKeyId in the request parameters establish when encryption was enabled and who enabled it. A Security Hub findings export filtered to this control across all active regions covers the continuous compliance requirement.

Framework-specific interpretation

SOC 2: CC6.1 and CC6.7 are what SOC 2 Type II auditors reach for when reviewing messaging infrastructure. Evidence of a KMS key ARN on every in-scope topic, combined with key policy documentation, satisfies both criteria.

PCI DSS v4.0: Requirement 3.4 expects stored account data to be rendered unreadable. If SNS topics carry card data in event payloads, KMS encryption addresses that expectation. Note that Requirement 3.5 separately governs the key management practices around those KMS keys, so key policy and rotation procedures need their own controls.

HIPAA Omnibus Rule 2013: Under 45 CFR 164.312(a)(2)(iv), encryption is an addressable implementation specification for ePHI at rest. SNS topics carrying patient notification events or health system integration messages need KMS encryption in place. An unencrypted messaging layer will surface as a gap in a HIPAA risk analysis.

GDPR: Article 32(1)(a) lists encryption as one of the appropriate technical measures for protecting personal data. When SNS topics carry information about EU data subjects, KMS at rest limits exposure and can affect the breach notification threshold under Article 34.

NIST SP 800-53 Rev 5: SC-28 requires cryptographic mechanisms to prevent unauthorized disclosure of information at rest. KMS on SNS provides FIPS 140-2 validated encryption for message data stored within the service, satisfying this control.

NIST Cybersecurity Framework v2.0: PR.DS in the Protect function calls for data-at-rest protections scaled to the organization's risk posture. Encrypting SNS topics covers message payloads stored temporarily during retry and fanout operations.

FedRAMP Moderate Baseline Rev 4: At the Moderate baseline, SC-28 mandates FIPS-validated encryption for all federal data at rest. KMS meets that bar when configured with FIPS-validated modules, making this the standard approach for FedRAMP-authorized SNS usage.

Tool mappings

Use these identifiers to cross-reference this control across tools, reports, and evidence.

  • Compliance.tf Control: sns_topic_encrypted_at_rest

  • AWS Config Managed Rule: SNS_ENCRYPTED_KMS

  • Checkov Check: CKV_AWS_26

  • Powerpipe Control: aws_compliance.control.sns_topic_encrypted_at_rest

  • Prowler Check: sns_topics_kms_encryption_at_rest_enabled

  • AWS Security Hub Control: SNS.1

  • KICS Queries: 28545147-2fc6-42d5-a1f9-cf226658e591, b1a72f66-2236-4f3b-87ba-0da1b366956f

  • Trivy Checks: AWS-0095, AWS-0136

Last reviewed: 2026-03-09