クロスアカウントで Amazon SNS と SQS を使ったメッセージ配信で不用意な配信停止を抑止する

Amazon SNS と Amazon SQS を使った Pub/Sub Messaging において、SQS 側で確認できるメッセージの中に配信を任意で止めることができる URL ( `UnsubscribeURL` ) を含んでいることに注意してください。この URL を知っていれば誰でもアクセスしてメッセージ配信を止めることができます。当記事では不用意にメッセージ配信を停止させないための対策をお伝えします。
2019.12.10

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Amazon SNS と SQS を使った Pub/Sub Messaging の注意点

異なるシステム間のやり取りを疎結合に実現するため、 Amazon SNS を使ったメッセージ送信と Amazon SQS でメッセージをポーリングする手法 (Pub/Sub Messaging) があります。

異なる AWS アカウントでこの手法を実現する方法として、弊社の以下のブログが参考になります。

AWSアカウントでSNSとSQSを連携させるには

私が開発に携わる prismatix でも、 prismatix を利用していただいているクライアントシステム向けに非同期で発生するイベントの通知に同様の手法を利用しております。

メッセージ受信側がメッセージ配信停止 (Unsubscribe) できることに注意

注意しなければいけないのは、 SQS で確認できるメッセージの中に、配信を任意で止めることができる URL ( UnsubscribeURL ) を含んでいることです。この URL を知っていれば誰でもアクセスしてメッセージ配信を止めることができます。

URL を不用意にクリックした場合、本来メッセージを受信して動くはずの後続の処理が止まります。

UnsubscribeURL は、 メッセージ受信する側が通知を止める機能 として、本来あるべきものです。通知を止めるケースを想定していれば本記事で紹介する対策は不要です。

メッセージ配信を不用意に停止させないためには、 SNS のサブスクリプション設定時に以下の考慮が必要となります。

  1. サブスクリプション作成時、 raw メッセージ配信を有効化する
  2. UnsubscribeURL のリンクを無効化する

Unsubscribe の回避策

1. サブスクリプション作成時、 raw メッセージ配信を有効化する

SNS のサブスクリプションで SQS 宛に送信するメッセージには、通常以下のようなメタデータを含みます。この中に UnsubscribeURL も含んでいます。

※以下、 受信者が Amazon SQS キューの場合のシステム間メッセージングに Amazon SNS を使用する - Amazon Simple Notification Service より抜粋

メッセージサンプル

{
   "Type" : "Notification",
   "MessageId" : "63a3f6b6-d533-4a47-aef9-fcf5cf758c76",
   "TopicArn" : "arn:aws:sns:us-west-2:123456789012:MyTopic",
   "Subject" : "Testing publish to subscribed queues",
   "Message" : "Hello world!",
   "Timestamp" : "2012-03-29T05:12:16.901Z",
   "SignatureVersion" : "1",
   "Signature" : "EXAMPLEnTrFPa37tnVO0FF9Iau3MGzjlJLRfySEoWz4uZHSj6ycK4ph71Zmdv0NtJ4dC/El9FOGp3VuvchpaTraNHWhhq/OsN1HVz20zxmF9b88R8GtqjfKB5woZZmz87HiM6CYDTo3l7LMwFT4VU7ELtyaBBafhPTg9O5CnKkg=",
   "SigningCertURL" : "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-f3ecfb7224c7233fe7bb5f59f96de52f.pem",
   "UnsubscribeURL" : "https://sns.us-west-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-west-2:123456789012:MyTopic:c7fe3a54-ab0e-4ec2-88e0-db410a0f2bee"
}

SNS のサブスクリプション作成または編集時、 raw メッセージ配信の有効化 にチェックを入れることで、メッセージの内容は上記の Message のみとなります。

rawメッセージ配信の場合のメッセージサンプル

Hello world!

既存のサブスクリプションで raw メッセージ配信の有効化する場合の注意点

raw メッセージ配信の有効化をすると、メッセージの送信形式が変わります。 メッセージ受信側がメタデータの情報を取得している場合、または json の Message キーから内容を取得する前提で実装をしている場合は、その処理を修正する必要があります。

2. UnsubscribeURL のリンクを無効化する

raw メッセージだけではなく MessageIdTimestamp 等のメタデータも欲しい。けど不用意にメッセージ受信を止めることは避けたい。その場合は UnsubscribeURL を無効化することを推奨します。

サブスクリプションを作成してSubscriptionConfirmation のメッセージ TopicArnToken を確認

SNS のサブスクリプションを作成してエンドポイントに SQS の ARN を指定した後、 受信先となる SQS に確認用メッセージ SubscriptionConfirmation を送っています。

SubscriptionConfirmationサンプル

{
  "Type" : "SubscriptionConfirmation",
  "MessageId" : "e0b8cad8-c005-40ad-8215-fe890db47a7b",
  "Token" : "eSMolFvdZg9O9koUU3GIS2nYG5YTGBeJoFOxEn23UycuikYcNJjZ0JD8yXkT23qS",
  "TopicArn" : "arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:SubscribeTest",
  "Message" : "You have chosen to subscribe to the topic arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:SubscribeTest.\nTo confirm the subscription, visit the SubscribeURL included in this message.",
  "SubscribeURL" : "https://sns.ap-northeast-1.amazonaws.com/?Action=ConfirmSubscription&TopicArn=arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:SubscribeTest&Token=eSMolFvdZg9O9koUU3GIS2nYG5YTGBeJoFOxEn23UycuikYcNJjZ0JD8yXkT23qS",
  "Timestamp" : "2019-12-09T11:54:57.672Z",
  "SignatureVersion" : "1",
  "Signature" : "9gRUt4LoDVrkcXfTJgAG2DnYaJqJ82yYteXvyjFZXjdkIsWFyGDqYlXoc7GQNt2D",
  "SigningCertURL" : "https://sns.ap-northeast-1.amazonaws.com/SimpleNotificationService-kfMCqaWKzxFsbVPf.pem"
}

この中の TopicArnToken の情報を拾います。 SNS のトピック・サブスクリプションを所有するアカウントで、 次の AWS CLI コマンドを実行します。

$ aws sns confirm-subscription --token eSMolFvdZg9O9koUU3GIS2nYG5YTGBeJoFOxEn23UycuikYcNJjZ0JD8yXkT23qS --topic-arn arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:SubscribeTest --authenticate-on-unsubscribe true --region ap-northeast-1
{
    "SubscriptionArn": "arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:SubscribeTest:a58ea867-b92c-4327-b8fa-52c6028910ad"
}

その後、SNS トピックで発行したメッセージにある UnsubscribeURL にアクセスすると以下のエラーが確認できます。

<ErrorResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
<Error>
<Type>Sender</Type>
<Code>AuthorizationError</Code>
<Message>
This subscription may only be deleted by an authorized user.
</Message>
</Error>
<RequestId>637ea3b5-4685-4c5c-8928-a66a91cb6806</RequestId>
</ErrorResponse>

SNS トピックで発行したメッセージは、引き続きサブスクライブ可能な状態を維持しています。

Unsubscribe 状態にした後に UnsubscribeConfirmation のメッセージ TopicArnToken を確認

既存のサブスクリプションで Unsubscribe を実行した場合に、SQS側で受信した UnsubscribeConfirmation のメッセージの内容を利用して再度 confirm-subscription のコマンドでサブスクリプションの復旧と同時に UnsubscribeURL の無効化を実行することができます。

UnsubscribeConfirmationサンプル

{
  "Type" : "UnsubscribeConfirmation",
  "MessageId" : "0036e5f8-ef1e-4931-b546-a6ac3f373ee0",
  "Token" : "cvfq9RYcp1ZlK9UrNSMNZ5zmYkMUpPpXIY3I4CA7jwE6Hxd5YZXJdoD6s64h6qXJ",
  "TopicArn" : "arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:SubscribeTest",
  "Message" : "You have chosen to deactivate subscription arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:SubscribeTest:zzzz.\nTo cancel this operation and restore the subscription, visit the SubscribeURL included in this message.",
  "SubscribeURL" : "https://sns.ap-northeast-1.amazonaws.com/?Action=ConfirmSubscription&TopicArn=arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:SubscribeTest&Token=cvfq9RYcp1ZlK9UrNSMNZ5zmYkMUpPpXIY3I4CA7jwE6Hxd5YZXJdoD6s64h6qXJ",
  "Timestamp" : "2019-12-09T12:05:16.765Z",
  "SignatureVersion" : "1",
  "Signature" : "9gRUt4LoDVrkcXfTJgAG2DnYaJqJ82yYteXvyjFZXjdkIsWFyGDqYlXoc7GQNt2D",
  "SigningCertURL" : "https://sns.ap-northeast-1.amazonaws.com/SimpleNotificationService-kfMCqaWKzxFsbVPf.pem"
}

UnsubscribeURL のリンク無効化は、Confirmation が完了したサブスクリプションでは実行できないことに注意

既に Confirmation が完了している状態では UnsubscribeURL のリンク無効化を実行できません。 既存のサブスクリプションで今回の無効化手順を行う際は、サブスクリプションの再作成 つまり 一時的にメッセージ配信を止めることになりますのでご留意ください。

参考情報

SQSではなくメール通知を使った配信の場合も同様の対処ができます。上記を参考にしてください。

prismatix をより良いサービスにしていくエンジニアを募集しています

さいごに宣伝ですが、私が所属する事業開発部では冒頭でご紹介した prismatix (EC/CRM向けAPIプラットフォーム)を開発・運用しております。良いサービスを提供し続けるため、一緒に開発・運用するメンバーを現在募集しております。

興味がありましたら採用サイトの 事業開発部 の募集要項をご確認の上お問い合わせいただけるとありがたいです。 ?‍♂️

募集要項 | クラスメソッド採用サイト

よろしくおねがいします。