こしあん
2025-01-07

Amazon SNSのメッセージ保護を試す


6{icon} {views}


Terraformで作成したSNSトピックにデータ保護ポリシーを適用し、メールアドレスなどの個人情報を自動マスキングするテストをしてみました。結果として、メールアドレスはマスクされたが日本語の氏名には対応が弱いことがわかりました。

はじめに

  • SCSを勉強していたら、SNSの機能でPIIに対して自動的にマスキングをかけられるものがあったので試してみた
  • 名前やメールアドレスといった個人情報のほかにも、IPアドレスなどもマッチングも可能
  • 「データ保護ポリシー」というのをSNSに対してかけることで実装し、マスクだけでなく「◯◯があったら拒否」みたいなフローも可能
  • インバウンドとアウトバンドの両方にかけられるが、インバウンドを試してみた

Amazon SNS データ保護ポリシーの理解より

参考情報

Terraformコード

説明するよりTerraformのコードを見せたほうが早そうなので先にコードを添付。

# SNS トピックの作成
resource "aws_sns_topic" "example_sns_topic" {
  name = "example-sns-topic-with-protection"
}

# PIIのインバウンドメッセージ保護
resource "aws_sns_topic_data_protection_policy" "pii_reduction" {
  arn = aws_sns_topic.example_sns_topic.arn
  policy = jsonencode(
    {
      "Description" = "Example data protection policy (PII)"
      "Name"        = "example-pii-reduction-policy"
      "Statement" = [
        {
          "DataDirection" = "Inbound"
          "DataIdentifier" = [
            "arn:aws:dataprotection::aws:data-identifier/EmailAddress",
            "arn:aws:dataprotection::aws:data-identifier/Name"
          ]
          "Operation" = {
            "Deidentify" = {
              "MaskConfig" = {
                "MaskWithCharacter": "#"
              }
            }
          }
          "Principal" = [
            "*",
          ]
          "Sid" = "__reduction_statement_1"
        },
      ]
      "Version" = "2021-06-01"
    }
  )
}


# SNS サブスクリプションの作成
resource "aws_sns_topic_subscription" "example_sns_subscription" {
  topic_arn = aws_sns_topic.example_sns_topic.arn
  protocol  = "email"
  endpoint  = var.email_address
}

aws_sns_topic_data_protection_policyがデータ保護の正体で、IAMポリシーと似たようにポリシーで定義する。これがなかったら普通のSNSと同じ。

DataIdentifierが識別パターンで、今回はメールアドレスと氏名にしている。AWSが定義したマネージドデータ識別子があり、それを利用している。Operation以下が「どう処理するのか?」で、Deidentifyがマスクするというもの。

関連ドキュメント

ポリシーの書き方は以下のドキュメントを参照

Amazon SNS データ保護ポリシーの理解

定義済みのマネージドデータ識別子は以下のドキュメントを参照

Using managed data identifiers in Amazon SNS

テスト送信

以下のくっそ適当なコードでSNSトピックに送信してみる。

import boto3

def get_topic_arn(sns_client, topic_name):
    """
    list_topics の結果から topic_name と一致する TopicArn を返す。
    見つからない場合は None を返す。
    """
    next_token = None
    while True:
        if next_token:
            response = sns_client.list_topics(NextToken=next_token)
        else:
            response = sns_client.list_topics()

        for topic_info in response.get('Topics', []):
            arn = topic_info['TopicArn']
            # ARNの末尾(トピック名)と一致するかチェック
            if arn.split(':')[-1] == topic_name:
                return arn

        next_token = response.get('NextToken')
        if not next_token:
            # 次のトークンが無ければ、全てのトピックを探索済み
            break

    return None

def publish_to_sns():
    session = boto3.Session(profile_name='hogehoge')
    sns_client = session.client('sns')

    # create topicでトピックが存在している場合は、ARNを動的に取得できる
    topic_name = "example-sns-topic-with-protection"  # トピック名を指定
    topic_arn = get_topic_arn(sns_client, topic_name)

    # 名前が漢字だと反応しない
    response = sns_client.publish(
        TopicArn=topic_arn,
        Message="""
お問い合わせユーザー:石破茂
メールアドレス:shigeru.ishiba@example.com
お問い合わせ内容:新年明けましておめでとうございます
""",
        Subject="テスト通知",
    )

    print("MessageId:", response["MessageId"])
    print("Publish Response:", response)



    # 名前がローマ字だと反応する
    response = sns_client.publish(
        TopicArn=topic_arn,
        Message="""
お問い合わせユーザー:Shigeru Ishiba
メールアドレス:shigeru.ishiba@example.com
お問い合わせ内容:新年明けましておめでとうございます
""",
        Subject="テスト通知",
    )

    print("MessageId:", response["MessageId"])
    print("Publish Response:", response)

if __name__ == "__main__":
    publish_to_sns()

氏名を「石破茂」「Shigeru Ishiba」の両方のパターンで送信

  • 両方ともメールアドレスはマスクされている
  • 名前は漢字だとマスクされない。ローマ字だとOK

漢字の名前が貫通してしまうのは、推測ではあるが裏でMacie使っていて、Macieが日本語対応弱いというところからなのかもしれない。世界共通の要素をマスクするなら一定使えそうな印象。

料金

SNSのデータ保護

東京リージョンの場合

発行および配信メッセージスキャンおよび保護 (ブロックまたは非識別化) は、ペイロードデータ 1 GB あたり USD 0.12 です
監査レポートは、生成された監査レポートデータ 1 GB あたり USD 0.29 です

https://aws.amazon.com/jp/sns/pricing/

Macieの場合

参考までにMacieの場合。Macieはバケット単位でスキャンするなら安いが、それ以外の使い方だともしかしたらSNSのデータ保護のほうが安い説はある。

バケットのインベントリとモニタリング 料金
バケット/月 バケットあたり USD 0.10

月ごとにデータを検査し、対象自動機密データ検出を実現 料金
最初の 50 TB/月 GB あたり USD 1.25
追加の 450 TB/月 GB あたり USD 0.63

https://aws.amazon.com/jp/macie/pricing/

所感

  • 日本対応が弱くて歯がゆい感じはするが、InboundだけでなくOutboundと組み合わせて「Slackに通知するときだけ保護する」みたいなFanoutと組み合わせると便利そう
  • 属性ベースのポリシーならもっとうまく機能するかも?


Shikoan's ML Blogの中の人が運営しているサークル「じゅ~しぃ~すくりぷと」の本のご案内

技術書コーナー

北海道の駅巡りコーナー


Add a Comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です