SNS Topics Not Publicly Accessible
Overview
This check ensures that Amazon SNS (Simple Notification Service) topics are not publicly accessible. An SNS topic is considered publicly accessible when its access policy allows any AWS principal (using * or anonymous access) to perform actions without restrictive conditions.
SNS topics should only be accessible to specific, trusted principals within your organization or account.
Risk
Publicly accessible SNS topics pose significant security risks:
- Data exposure: Unauthorized users can subscribe to the topic and intercept sensitive messages
- Message injection: Attackers can publish malicious or spoofed messages that may disrupt downstream workflows
- Cost impact: Bad actors can flood the topic with messages, causing unexpected AWS charges
- Compliance violations: Public access may violate regulatory requirements (PCI-DSS, HIPAA, SOC 2)
Remediation Steps
Prerequisites
- Access to the AWS Console with permissions to modify SNS topic policies
- Knowledge of which principals (users, roles, accounts) should have access to the topic
Required IAM permissions
You need the following IAM permissions:
sns:GetTopicAttributes- to view current topic policysns:SetTopicAttributes- to update the topic policysns:ListTopics- to list topics (optional, for discovery)
AWS Console Method
- Open the Amazon SNS Console
- In the left navigation, click Topics
- Select the topic that was flagged as publicly accessible
- Click the Access policy tab
- Click Edit
- In the policy editor, look for any statement where:
Principalis set to"*"or{"AWS": "*"}- There are no restrictive
Conditionelements
- Modify the policy to restrict access to specific principals. Replace
"*"with your account root or specific IAM roles:{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowAccountAccess",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<YOUR_ACCOUNT_ID>:root"
},
"Action": [
"sns:Publish",
"sns:Subscribe",
"sns:GetTopicAttributes"
],
"Resource": "arn:aws:sns:us-east-1:<YOUR_ACCOUNT_ID>:<TOPIC_NAME>"
}
]
} - Click Save changes
AWS CLI (optional)
Update Topic Policy via CLI
First, identify the topic ARN of the affected topic:
aws sns list-topics --region us-east-1
View the current policy to understand what needs to change:
aws sns get-topic-attributes \
--topic-arn arn:aws:sns:us-east-1:<ACCOUNT_ID>:<TOPIC_NAME> \
--region us-east-1 \
--query 'Attributes.Policy' \
--output text
Create a new restricted policy and apply it:
aws sns set-topic-attributes \
--topic-arn arn:aws:sns:us-east-1:<ACCOUNT_ID>:<TOPIC_NAME> \
--attribute-name Policy \
--attribute-value '{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowAccountAccess",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<ACCOUNT_ID>:root"
},
"Action": [
"sns:Publish",
"sns:Subscribe",
"sns:GetTopicAttributes"
],
"Resource": "arn:aws:sns:us-east-1:<ACCOUNT_ID>:<TOPIC_NAME>"
}
]
}' \
--region us-east-1
Replace <ACCOUNT_ID> and <TOPIC_NAME> with your actual values.
Alternative: Using a Policy File
For complex policies, save to a file first:
# Create the policy file
cat > sns-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowAccountAccess",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<ACCOUNT_ID>:root"
},
"Action": [
"sns:Publish",
"sns:Subscribe",
"sns:GetTopicAttributes"
],
"Resource": "arn:aws:sns:us-east-1:<ACCOUNT_ID>:<TOPIC_NAME>"
}
]
}
EOF
# Apply the policy
aws sns set-topic-attributes \
--topic-arn arn:aws:sns:us-east-1:<ACCOUNT_ID>:<TOPIC_NAME> \
--attribute-name Policy \
--attribute-value file://sns-policy.json \
--region us-east-1
CloudFormation (optional)
CloudFormation Template
Use this template to create an SNS topic with a restricted access policy:
AWSTemplateFormatVersion: '2010-09-09'
Description: SNS Topic with restricted access policy (not publicly accessible)
Parameters:
TopicName:
Type: String
Description: Name of the SNS topic
Default: my-secure-topic
Resources:
SecureSNSTopic:
Type: AWS::SNS::Topic
Properties:
TopicName: !Ref TopicName
SecureSNSTopicPolicy:
Type: AWS::SNS::TopicPolicy
Properties:
Topics:
- !Ref SecureSNSTopic
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: AllowAccountAccess
Effect: Allow
Principal:
AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
Action:
- sns:Publish
- sns:Subscribe
- sns:GetTopicAttributes
Resource: !Ref SecureSNSTopic
Outputs:
TopicArn:
Description: ARN of the SNS topic
Value: !Ref SecureSNSTopic
TopicName:
Description: Name of the SNS topic
Value: !GetAtt SecureSNSTopic.TopicName
Deploy the Stack
aws cloudformation deploy \
--template-file template.yaml \
--stack-name secure-sns-topic \
--parameter-overrides TopicName=my-secure-topic \
--region us-east-1
Update an Existing Topic
To remediate an existing topic, create a separate stack with just the policy:
AWSTemplateFormatVersion: '2010-09-09'
Description: Restricted policy for existing SNS topic
Parameters:
ExistingTopicArn:
Type: String
Description: ARN of the existing SNS topic to secure
Resources:
SecureSNSTopicPolicy:
Type: AWS::SNS::TopicPolicy
Properties:
Topics:
- !Ref ExistingTopicArn
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: AllowAccountAccess
Effect: Allow
Principal:
AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
Action:
- sns:Publish
- sns:Subscribe
- sns:GetTopicAttributes
Resource: !Ref ExistingTopicArn
Terraform (optional)
Terraform Configuration
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.0"
}
}
}
provider "aws" {
region = "us-east-1"
}
variable "topic_name" {
description = "Name of the SNS topic"
type = string
default = "my-secure-topic"
}
data "aws_caller_identity" "current" {}
resource "aws_sns_topic" "secure_topic" {
name = var.topic_name
}
resource "aws_sns_topic_policy" "secure_policy" {
arn = aws_sns_topic.secure_topic.arn
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowAccountAccess"
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
}
Action = [
"sns:Publish",
"sns:Subscribe",
"sns:GetTopicAttributes"
]
Resource = aws_sns_topic.secure_topic.arn
}
]
})
}
output "topic_arn" {
description = "ARN of the SNS topic"
value = aws_sns_topic.secure_topic.arn
}
Apply Configuration
terraform init
terraform plan
terraform apply
Import Existing Topic
To manage an existing topic with Terraform:
terraform import aws_sns_topic.secure_topic arn:aws:sns:us-east-1:<ACCOUNT_ID>:<TOPIC_NAME>
Verification
After applying the remediation:
- In the AWS Console, navigate to the SNS topic and check the Access policy tab
- Confirm that no statement has
Principalset to"*"without restrictive conditions - Re-run the Prowler check to confirm it passes:
prowler aws --check sns_topics_not_publicly_accessible --region us-east-1
CLI verification commands
Retrieve and inspect the policy:
aws sns get-topic-attributes \
--topic-arn arn:aws:sns:us-east-1:<ACCOUNT_ID>:<TOPIC_NAME> \
--region us-east-1 \
--query 'Attributes.Policy' \
--output text | jq .
Check for public principals in the policy:
aws sns get-topic-attributes \
--topic-arn arn:aws:sns:us-east-1:<ACCOUNT_ID>:<TOPIC_NAME> \
--region us-east-1 \
--query 'Attributes.Policy' \
--output text | jq '.Statement[] | select(.Principal == "*" or .Principal.AWS == "*")'
If the above command returns no output, there are no public principals in the policy.
Additional Resources
- Amazon SNS Access Control
- Amazon SNS API Permissions Reference
- IAM Policies and SNS Together
- Prowler SNS Check Documentation
Notes
-
Cross-account access: If you need to allow access from other AWS accounts, specify their account IDs explicitly rather than using
*. For example:"Principal": {"AWS": "arn:aws:iam::123456789012:root"} -
Service integrations: Some AWS services (like S3, CloudWatch, or EventBridge) need to publish to SNS topics. Use condition keys to scope access:
{
"Condition": {
"ArnLike": {
"aws:SourceArn": "arn:aws:s3:::my-bucket"
}
}
} -
Organization-wide access: To allow access from all accounts in your AWS Organization, use the
aws:PrincipalOrgIDcondition key instead of wildcards:{
"Condition": {
"StringEquals": {
"aws:PrincipalOrgID": "o-xxxxxxxxxx"
}
}
} -
Existing subscriptions: Removing public access will not affect existing subscriptions, but new subscription requests from unauthorized principals will be denied.