Amazon SNSのメッセージ保護を試す
Terraformで作成したSNSトピックにデータ保護ポリシーを適用し、メールアドレスなどの個人情報を自動マスキングするテストをしてみました。結果として、メールアドレスはマスクされたが日本語の氏名には対応が弱いことがわかりました。
目次
はじめに
- SCSを勉強していたら、SNSの機能でPIIに対して自動的にマスキングをかけられるものがあったので試してみた
- 名前やメールアドレスといった個人情報のほかにも、IPアドレスなどもマッチングも可能
- 「データ保護ポリシー」というのを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
がマスクするというもの。
関連ドキュメント
ポリシーの書き方は以下のドキュメントを参照
定義済みのマネージドデータ識別子は以下のドキュメントを参照
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の中の人が運営しているサークル「じゅ~しぃ~すくりぷと」の本のご案内
技術書コーナー
北海道の駅巡りコーナー