コンテンツにスキップ

SNS トピック

検証日: 2026-04-04 / リージョン: ap-northeast-1

概要

IAM Access Analyzer が SNS トピックの外部アクセスをどのように検出するかを検証する。

SNS トピックはトピックポリシーにより外部プリンシパルへのアクセスを許可できる。デフォルトのトピックポリシーは Principal: {"AWS": "*"} を含むが、Condition で自アカウントのみに制限されており、明示的にポリシーを変更しない限り外部アクセスは発生しない。トピックポリシーに Principal: "*"(Condition なし)を設定するとパブリックアクセスとして検出され、特定の外部アカウントを設定するとクロスアカウントアクセスとして検出される。

SNS トピックの外部アクセス制御レイヤー

レイヤーSNS トピックの状況
デフォルト保護なし(デフォルトポリシーは Condition で自アカウントのみに制限)
予防(SCP / RCP)予防コントロールなし(カスタム SCP で対応可能)
Security Hub CSPMSNS.4(パブリックアクセスのチェック)
IAM Access Analyzerパブリック + クロスアカウントの到達可能性を分析

結果

  • デフォルトのトピックポリシー: デフォルトポリシーは Principal: {"AWS": "*"} を含むが、Condition: {"StringEquals": {"AWS:SourceOwner": "<自アカウント ID>"}} で自アカウントのみに制限されている。外部アクセスは許可されていない
  • パブリックアクセスの検出: トピックポリシーに Principal: "*"(Condition なし)を追加すると、isPublic: true の finding が生成された。action は sns:Publish, sns:Subscribe
  • クロスアカウントアクセスの検出: 組織外アカウントをトピックポリシーに設定すると、isPublic: false の finding が生成された。principal に組織外アカウント ID が特定された
  • 予防コントロール: SNS トピックに対応する Control Tower の予防コントロールは存在しない。クロスアカウントアクセスの予防にはカスタム SCP での対応が必要
  • クリーンアップ: トピックポリシーからパブリック・クロスアカウントのステートメントを削除すると finding は RESOLVED になる

検証環境

検証環境の構成図

本記事のコマンドは、--profile 指定がない場合は Workload アカウントで実行する。IAM Access Analyzer の finding 確認は Audit アカウントで実行する。

検証の流れ

    flowchart LR
    A[1. 既存 finding の<br>確認] --> B[2. テスト用トピック<br>作成]
    B --> C[3. パブリックアクセス<br>検出の確認]
    C --> D[4. クロスアカウント<br>アクセス検出]
    D --> E[5. クリーンアップ]
  

1. 既存 finding の確認

テスト用トピックを作成する前に、デフォルトのトピックポリシーと既存の SNS トピック finding を確認する。

デフォルトのトピックポリシーの確認

SNS トピックのデフォルトポリシーを確認する。

aws sns get-topic-attributes \
  --topic-arn arn:aws:sns:ap-northeast-1:<Workload アカウント ID>:<トピック名> \
  --query 'Attributes.Policy' \
  --output text | python3 -m json.tool
{
    "Version": "2008-10-17",
    "Id": "__default_policy_ID",
    "Statement": [
        {
            "Sid": "__default_statement_ID",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": [
                "SNS:GetTopicAttributes",
                "SNS:SetTopicAttributes",
                "SNS:AddPermission",
                "SNS:RemovePermission",
                "SNS:DeleteTopic",
                "SNS:Subscribe",
                "SNS:ListSubscriptionsByTopic",
                "SNS:Publish"
            ],
            "Resource": "arn:aws:sns:ap-northeast-1:<Workload アカウント ID>:<トピック名>",
            "Condition": {
                "StringEquals": {
                    "AWS:SourceOwner": "<Workload アカウント ID>"
                }
            }
        }
    ]
}

デフォルトポリシーは Principal: {"AWS": "*"} を含むが、ConditionAWS:SourceOwner が自アカウント ID の場合のみに制限されている。この Condition により、外部アカウントからのアクセスは拒否される。

既存 finding の確認

aws accessanalyzer list-findings-v2 \
  --analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
  --filter '{"status": {"eq": ["ACTIVE"]}, "resourceType": {"eq": ["AWS::SNS::Topic"]}}' \
  --query 'findings[].{id:id,resource:resource,status:status}' \
  --region ap-northeast-1 \
  --profile Audit
[]

2. テスト用トピックの作成

外部プリンシパルへのアクセスを許可するトピックを作成する。検証目的のみで行い、ステップ 5 で削除する。

2 つのテスト用 SNS トピックを作成し、トピックポリシーで外部アクセスを設定する。デフォルトポリシーのステートメント(自アカウント管理用)は残したまま、外部アクセス用のステートメントを追加する。

パブリックアクセス検証用トピック

aws sns create-topic \
  --name iaa-sns-public-test
{
    "TopicArn": "arn:aws:sns:ap-northeast-1:<Workload アカウント ID>:iaa-sns-public-test"
}

トピックポリシーに Principal: "*"(Condition なし)のステートメントを追加する。

aws sns set-topic-attributes \
  --topic-arn arn:aws:sns:ap-northeast-1:<Workload アカウント ID>:iaa-sns-public-test \
  --attribute-name Policy \
  --attribute-value '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Sid": "default",
        "Effect": "Allow",
        "Principal": {"AWS": "*"},
        "Action": ["SNS:GetTopicAttributes", "SNS:SetTopicAttributes", "SNS:AddPermission", "SNS:RemovePermission", "SNS:DeleteTopic", "SNS:Subscribe", "SNS:ListSubscriptionsByTopic", "SNS:Publish"],
        "Resource": "arn:aws:sns:ap-northeast-1:<Workload アカウント ID>:iaa-sns-public-test",
        "Condition": {"StringEquals": {"AWS:SourceOwner": "<Workload アカウント ID>"}}
      },
      {
        "Sid": "AllowPublic",
        "Effect": "Allow",
        "Principal": "*",
        "Action": ["SNS:Publish", "SNS:Subscribe"],
        "Resource": "arn:aws:sns:ap-northeast-1:<Workload アカウント ID>:iaa-sns-public-test"
      }
    ]
  }'
(出力なし)

クロスアカウントアクセス検証用トピック

aws sns create-topic \
  --name iaa-sns-crossaccount-test
{
    "TopicArn": "arn:aws:sns:ap-northeast-1:<Workload アカウント ID>:iaa-sns-crossaccount-test"
}

トピックポリシーに組織外アカウントのステートメントを追加する。

aws sns set-topic-attributes \
  --topic-arn arn:aws:sns:ap-northeast-1:<Workload アカウント ID>:iaa-sns-crossaccount-test \
  --attribute-name Policy \
  --attribute-value '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Sid": "default",
        "Effect": "Allow",
        "Principal": {"AWS": "*"},
        "Action": ["SNS:GetTopicAttributes", "SNS:SetTopicAttributes", "SNS:AddPermission", "SNS:RemovePermission", "SNS:DeleteTopic", "SNS:Subscribe", "SNS:ListSubscriptionsByTopic", "SNS:Publish"],
        "Resource": "arn:aws:sns:ap-northeast-1:<Workload アカウント ID>:iaa-sns-crossaccount-test",
        "Condition": {"StringEquals": {"AWS:SourceOwner": "<Workload アカウント ID>"}}
      },
      {
        "Sid": "AllowCrossAccount",
        "Effect": "Allow",
        "Principal": {"AWS": "arn:aws:iam::<組織外アカウント ID>:root"},
        "Action": ["SNS:Publish", "SNS:Subscribe"],
        "Resource": "arn:aws:sns:ap-northeast-1:<Workload アカウント ID>:iaa-sns-crossaccount-test"
      }
    ]
  }'
(出力なし)

3. パブリックアクセスの検出

ポリシー変更後、IAM Access Analyzer が finding を生成・更新するまで最大 30 分かかる場合がある。

パブリックアクセス検証用トピック(Principal: "*"、Condition なし)の finding を確認する。

aws accessanalyzer list-findings-v2 \
  --analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
  --filter '{"status": {"eq": ["ACTIVE"]}, "resource": {"contains": ["iaa-sns-public-test"]}}' \
  --query 'findings[].{id:id,resource:resource,resourceType:resourceType,status:status}' \
  --region ap-northeast-1 \
  --profile Audit
[
    {
        "id": "<パブリックトピック finding ID>",
        "resource": "arn:aws:sns:ap-northeast-1:<Workload アカウント ID>:iaa-sns-public-test",
        "resourceType": "AWS::SNS::Topic",
        "status": "ACTIVE"
    }
]

finding の詳細を確認する。

aws accessanalyzer get-finding-v2 \
  --analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
  --id <パブリックトピック finding ID> \
  --region ap-northeast-1 \
  --profile Audit
{
    "findingDetails": [
        {
            "externalAccessDetails": {
                "action": ["sns:Publish", "sns:Subscribe"],
                "condition": {},
                "isPublic": true,
                "principal": {"AWS": "*"},
                "resourceControlPolicyRestriction": "NOT_APPLICABLE"
            }
        }
    ],
    "resource": "arn:aws:sns:ap-northeast-1:<Workload アカウント ID>:iaa-sns-public-test",
    "status": "ACTIVE",
    "resourceType": "AWS::SNS::Topic",
    "findingType": "ExternalAccess",
    "resourceOwnerAccount": "<Workload アカウント ID>",
    "id": "<パブリックトピック finding ID>"
}

以下を確認する。

  • isPublictrue
  • principal{"AWS": "*"}
  • actionsns:Publish, sns:Subscribe が含まれる
  • resourceControlPolicyRestrictionNOT_APPLICABLE

組織外アカウントからの実際のアクセス確認

組織外アカウントからパブリックトピックへの Publish が成功することを確認する。

aws sns publish \
  --topic-arn arn:aws:sns:ap-northeast-1:<Workload アカウント ID>:iaa-sns-public-test \
  --message "test" \
  --query '{MessageId:MessageId}' \
  --profile <組織外プロファイル> \
  --region ap-northeast-1
{
    "MessageId": "<メッセージ ID>"
}

4. クロスアカウントアクセスの検出

クロスアカウントアクセス検証用トピックの finding を確認する。

aws accessanalyzer list-findings-v2 \
  --analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
  --filter '{"status": {"eq": ["ACTIVE"]}, "resource": {"contains": ["iaa-sns-crossaccount-test"]}}' \
  --query 'findings[].{id:id,resource:resource,resourceType:resourceType,status:status}' \
  --region ap-northeast-1 \
  --profile Audit
[
    {
        "id": "<クロスアカウントトピック finding ID>",
        "resource": "arn:aws:sns:ap-northeast-1:<Workload アカウント ID>:iaa-sns-crossaccount-test",
        "resourceType": "AWS::SNS::Topic",
        "status": "ACTIVE"
    }
]

finding の詳細を確認する。

aws accessanalyzer get-finding-v2 \
  --analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
  --id <クロスアカウントトピック finding ID> \
  --region ap-northeast-1 \
  --profile Audit
{
    "findingDetails": [
        {
            "externalAccessDetails": {
                "action": ["sns:Publish", "sns:Subscribe"],
                "condition": {},
                "isPublic": false,
                "principal": {"AWS": "<組織外アカウント ID>"},
                "resourceControlPolicyRestriction": "NOT_APPLICABLE"
            }
        }
    ],
    "resource": "arn:aws:sns:ap-northeast-1:<Workload アカウント ID>:iaa-sns-crossaccount-test",
    "status": "ACTIVE",
    "resourceType": "AWS::SNS::Topic",
    "findingType": "ExternalAccess",
    "resourceOwnerAccount": "<Workload アカウント ID>",
    "id": "<クロスアカウントトピック finding ID>"
}

以下を確認する。

  • isPublicfalse
  • principal に組織外アカウント ID が特定されている
  • actionsns:Publish, sns:Subscribe が含まれる
  • resourceControlPolicyRestrictionNOT_APPLICABLE

組織外アカウントからの実際のアクセス確認

組織外アカウントからクロスアカウントトピックへの Publish が成功することを確認する。

aws sns publish \
  --topic-arn arn:aws:sns:ap-northeast-1:<Workload アカウント ID>:iaa-sns-crossaccount-test \
  --message "test" \
  --query '{MessageId:MessageId}' \
  --profile <組織外プロファイル> \
  --region ap-northeast-1
{
    "MessageId": "<メッセージ ID>"
}

5. クリーンアップ

トピックポリシーのリセット

トピックポリシーからパブリック・クロスアカウントのステートメントを削除し、デフォルトポリシー(自アカウントのみ)に戻す。

aws sns set-topic-attributes \
  --topic-arn arn:aws:sns:ap-northeast-1:<Workload アカウント ID>:iaa-sns-public-test \
  --attribute-name Policy \
  --attribute-value '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Sid": "default",
        "Effect": "Allow",
        "Principal": {"AWS": "*"},
        "Action": ["SNS:GetTopicAttributes", "SNS:SetTopicAttributes", "SNS:AddPermission", "SNS:RemovePermission", "SNS:DeleteTopic", "SNS:Subscribe", "SNS:ListSubscriptionsByTopic", "SNS:Publish"],
        "Resource": "arn:aws:sns:ap-northeast-1:<Workload アカウント ID>:iaa-sns-public-test",
        "Condition": {"StringEquals": {"AWS:SourceOwner": "<Workload アカウント ID>"}}
      }
    ]
  }'
(出力なし)
aws sns set-topic-attributes \
  --topic-arn arn:aws:sns:ap-northeast-1:<Workload アカウント ID>:iaa-sns-crossaccount-test \
  --attribute-name Policy \
  --attribute-value '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Sid": "default",
        "Effect": "Allow",
        "Principal": {"AWS": "*"},
        "Action": ["SNS:GetTopicAttributes", "SNS:SetTopicAttributes", "SNS:AddPermission", "SNS:RemovePermission", "SNS:DeleteTopic", "SNS:Subscribe", "SNS:ListSubscriptionsByTopic", "SNS:Publish"],
        "Resource": "arn:aws:sns:ap-northeast-1:<Workload アカウント ID>:iaa-sns-crossaccount-test",
        "Condition": {"StringEquals": {"AWS:SourceOwner": "<Workload アカウント ID>"}}
      }
    ]
  }'
(出力なし)

finding の確認

ポリシーリセット後、finding が RESOLVED になることを確認する。

aws accessanalyzer list-findings-v2 \
  --analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
  --filter '{"resourceType": {"eq": ["AWS::SNS::Topic"]}}' \
  --query 'findings[].{id:id,resource:resource,status:status}' \
  --region ap-northeast-1 \
  --profile Audit
[]

テスト用トピックの削除

aws sns delete-topic \
  --topic-arn arn:aws:sns:ap-northeast-1:<Workload アカウント ID>:iaa-sns-public-test
(出力なし)
aws sns delete-topic \
  --topic-arn arn:aws:sns:ap-northeast-1:<Workload アカウント ID>:iaa-sns-crossaccount-test
(出力なし)

Amazonアソシエイトリンク