クロスアカウントで Amazon SNS と SQS を使ったメッセージ配信で不用意な配信停止を抑止する
Amazon SNS と SQS を使った Pub/Sub Messaging の注意点
異なるシステム間のやり取りを疎結合に実現するため、 Amazon SNS を使ったメッセージ送信と Amazon SQS でメッセージをポーリングする手法 (Pub/Sub Messaging) があります。
異なる AWS アカウントでこの手法を実現する方法として、弊社の以下のブログが参考になります。
私が開発に携わる prismatix でも、 prismatix を利用していただいているクライアントシステム向けに非同期で発生するイベントの通知に同様の手法を利用しております。
メッセージ受信側がメッセージ配信停止 (Unsubscribe) できることに注意
注意しなければいけないのは、 SQS で確認できるメッセージの中に、配信を任意で止めることができる URL ( UnsubscribeURL
) を含んでいることです。この URL を知っていれば誰でもアクセスしてメッセージ配信を止めることができます。
URL を不用意にクリックした場合、本来メッセージを受信して動くはずの後続の処理が止まります。
※ UnsubscribeURL
は、 メッセージ受信する側が通知を止める機能 として、本来あるべきものです。通知を止めるケースを想定していれば本記事で紹介する対策は不要です。
メッセージ配信を不用意に停止させないためには、 SNS のサブスクリプション設定時に以下の考慮が必要となります。
- サブスクリプション作成時、 raw メッセージ配信を有効化する
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
のみとなります。
Hello world!
既存のサブスクリプションで raw メッセージ配信の有効化する場合の注意点
raw メッセージ配信の有効化をすると、メッセージの送信形式が変わります。
メッセージ受信側がメタデータの情報を取得している場合、または json の Message
キーから内容を取得する前提で実装をしている場合は、その処理を修正する必要があります。
2. UnsubscribeURL のリンクを無効化する
raw メッセージだけではなく MessageId
や Timestamp
等のメタデータも欲しい。けど不用意にメッセージ受信を止めることは避けたい。その場合は UnsubscribeURL
を無効化することを推奨します。
サブスクリプションを作成してSubscriptionConfirmation
のメッセージ TopicArn
と Token
を確認
SNS のサブスクリプションを作成してエンドポイントに SQS の ARN を指定した後、 受信先となる SQS に確認用メッセージ 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" }
この中の TopicArn
と Token
の情報を拾います。
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
のメッセージ TopicArn
と Token
を確認
既存のサブスクリプションで Unsubscribe を実行した場合に、SQS側で受信した UnsubscribeConfirmation
のメッセージの内容を利用して再度 confirm-subscription
のコマンドでサブスクリプションの復旧と同時に UnsubscribeURL
の無効化を実行することができます。
{ "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 のリンク無効化を実行できません。 既存のサブスクリプションで今回の無効化手順を行う際は、サブスクリプションの再作成 つまり 一時的にメッセージ配信を止めることになりますのでご留意ください。
参考情報
- メーリングリストの受信者がSNS トピックからリストのサブスクリプション解除ができないようにする
- Amazon SNS メール通知の「unsubscribe」リンクを無効化する | Developers.IO
- SNSのunsubscribeリンクを無効化するのを楽にするスクリプト書いたった | Developers.IO
SQSではなくメール通知を使った配信の場合も同様の対処ができます。上記を参考にしてください。
prismatix をより良いサービスにしていくエンジニアを募集しています
さいごに宣伝ですが、私が所属する事業開発部では冒頭でご紹介した prismatix (EC/CRM向けAPIプラットフォーム)を開発・運用しております。良いサービスを提供し続けるため、一緒に開発・運用するメンバーを現在募集しております。
興味がありましたら採用サイトの 事業開発部 の募集要項をご確認の上お問い合わせいただけるとありがたいです。 ?♂️
よろしくおねがいします。