📝

Amazon SESのBounce/Complaintのトラッキング方法

2024/06/04に公開

Amazon SESとは

Amazon SES(Simple Email Service)とは、AWSにおけるメール配信サービスです。
例えばEC2やLambda等の各AWSサービスと連携してメールを送信する際に用いられます。

バウンス率、苦情率

SESでメールを送信する際に、メールサーバなどの障害によりメールの配信が失敗する場合や、送信されたメールの受け取りを受取人が拒否した場合(迷惑メールに登録された場合など)等何らかの理由でメールが配信できない場合があります。

そういった場合にはbounceまたはcomplaintと呼ばれる状態が発生します。

  • bounce(何かしらの理由で相手にメールが配信できなかった場合)

    • Hard bounce(永続的に配信不可):メールアドレスが存在しない場合等
    • Soft bounce(一時的に配信不可):メールボックスが満杯である、サーバー障害が発生している等
  • complaint(苦情によりメール配信ができなかった場合)

このbounceとcomplaintはSESのサービスを扱うにあたって重要な要素となります。
SESではbounce率を5%未満に保つことが推奨されており、10%を超えるとメール送信機能が一時停止される可能性があります。
同様にcomplaint率を0.1%未満に保つことが推奨されており、0.5%を超えると同様にメール送信機能が一時停止される可能性があります。

つまりどういうことかというとSESサービス自体が悪用されたり、AWSの評判を落とすようなことをする(スパム攻撃)をするとサービスを停止しますよということですね。

SESを扱う上ではこのbounce率とcomplaint率を閾値以下で保つことが必要不可欠となります。

実際に対応したこと

詳細は伏せますが、過去対応した案件でbounce率とcomplaint率が一時的に高まる事象が発生しました。
幸いなことにその後低下したことで問題になることはありませんでしたが、SESの送信状況をトラッキングすることが必要となりました。

簡単なイメージとしては以下のようなものです。

  1. SESでメール送信
  2. SNSを介してLambdaにSESの送信結果を通知
  3. Lambdaで送信先、送信元、Bounce理由等のデータをDynamoDBに保存
  4. SESでメール送信時にDynamoDBからデータを取得し条件に合致した場合は送信をブロック

実際にはメールアドレスのマスク処理だったり、送信ブロック時の閾値判定等考慮しましたがざっくり上記の対応を実施しました。

というわけで自身の振り返りという意味もかねてSES Bounce/Complaint対策に必要なSES送信結果をDynamoDBに格納する処理をまとめておきます。

設定方法

1.SESの送信結果をSNSに連携する

  1. SESより該当のドメインを選択、通知セクションのフィードバック通知の編集を押下

  2. 事前に作成したSES-notifyを選択

2.SNSからLambdaをキックする

  1. Lambdaの作成

  2. SNSのサブスクリプションの追加

3.LambdaからDynamoDBにBounce情報を格納する

今回、Bounce用とComplaint用に2つのテーブルを準備しました。

index.mjs
import {DynamoDB} from '@aws-sdk/client-dynamodb';
import {DynamoDBDocument} from '@aws-sdk/lib-dynamodb';

const dynamo = DynamoDBDocument.from(new DynamoDB());


export const handler = async(event) => {
	// SNSメッセージを解析
	console.log(event.Records[0]);
	const snsMessage = JSON.parse(event.Records[0].Sns.Message);
	const notificationType = snsMessage.notificationType;
	let param = {};

	if (notificationType == "Bounce") {

		const bounce = snsMessage.bounce;
		const mail = snsMessage.mail;


		param = {
			TableName: "SES-Bounce",
			Item: {
				"toaddr": bounce.bouncedRecipients[0].emailAddress,
				"fromaddr": mail.source,
				"bounceType": bounce.bounceType,
				"bounceSubType": bounce.bounceSubType,
				"timestamp": mail.timestamp,
			}
		};
	} else {

		const complaint = snsMessage.complaint;
		const mail = snsMessage.mail;

		param = {
			TableName: "SES-Complaint",
			Item: {
				"toaddr": complaint.complainedRecipients[0].emailAddress,
				"fromaddr": mail.source,
				"complaintFeedbackType": complaint.complaintFeedbackType,
				"timestamp": mail.timestamp,
			}
		};

	}

	try {
		await dynamo.put(param);
		return {
			statusCode: 200,
			body: JSON.stringify({
				message: '正常',
				data: param
			})
		};
	} catch (error) {
		console.error('失敗', error);
		return {
			statusCode: 500,
			body: JSON.stringify({
				message: '失敗',
				error: error.message
			})
		};
	}

};

4.実行結果

  1. SESのシミュレータよりテストメールの送信

  2. Bounce情報格納用テーブルにデータの格納を確認

Complaintも同様の手順でシミュレータからテストメールを送信することでComplaint情報格納用テーブルにデータが格納されます。

まとめと次回

以上がBounce情報格納までの流れとなります。
格納した情報をもとにSESのメール送信ブロックするにはいくつか考慮すべき事項があります。

  1. どのバウンス種別でメールをブロックするか
  2. バウンス種別でブロックする際の閾値はどうするか(例えば特定のメールアドレス宛にUndeterminedが24時間以内に3回発生したらブロック等)
  3. 送信をブロックした際にユーザ側への通知はどうするか
  4. GDPR等の法令対応の取り扱い

これ以外にも様々な考慮事項があります。
適用するサービスやシステムの要件に応じて判断しながら対策を行ってください。

次回はDynamoDBに格納したデータの参照方法をまとめます。

Discussion