AWS SES メール送信環境のセキュリティ

# はじめに
Amazon SES (以下SES) を使えば、Linuxを立ててPostfixを入れて設定して・・・などという面倒なことをしなくても、サーバレスで簡単にメール送信環境 (メールサーバのサーバレス版) を構築できます。
SESを使ってメールを送るには、AWS側で以下の3ステップを踏む必要があります。

1. サンドボックスの解除
2. SMTP Credentialの発行
3. **セキュリティ (認証・アクセス許可等) の設定**

今回、以下の「目指したい姿」を満たすため、セキュリティの設定をどうすれば良いのか、検証によって導いてみました。

# 目指したい姿

今回、SESでやりたかったことは以下です。

* メール送信用途で使う。
* 送信を許可する唯一のドメインを「test1.com」とすること。
    * 「test1.com」以外のドメインは全て拒否されること。
* Eメールアドレスは、個人につき1つ割り当てられ、個人は自分のメールアドレスだけを使用できること。
* 全ての利用者は、管理者が払い出したEメールアドレス以外を名乗って使用できないこと。
* SESから送信されていることを隠蔽すること。

# SESのセキュリティ
[SESのマネジメントコンソール](https://console.aws.amazon.com/ses/home)を開くと「Identity Management」という項目があります。
この項目で、SESのセキュリティ (アクセス許可) を設定できます。

## 設定できるアクセス許可項目
「Domains」と「Email Addresses」の2項目を設定できます。
後述の検証結果でも分かりますが、これらはホワイトリスト形式です。

### Domains
ここに登録したドメインをFromとするメールの送信を許可します。
ドメインは、DNSのゾーンに所定のTXTレコードを追加し、SESで承認される必要があります。
このため、**他人が持っているドメインを勝手に登録してFromに使うことはできません。**

### Email Addresses
ここに登録したメールアドレスをFromとするメールの送信を許可します。
メールアドレスは、SESから送信される確認メールのリンクを押すことで、SESで承認される必要があります。
このため、**他人が持っているメールアドレスを勝手に登録してFromに使うことはできません。**

## デフォルトの状態
まずはデフォルトの状態を見てみました。
サンドボックスを解除し、SMTP Credentialを発行しただけの状態では、DomainsとEmail Addressには何も登録されていません。

**「Domains」**
<img width=500px src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211986/eeafa487-2e03-0d58-335a-c03a3ab08c06.png>
**「Email Addresses」**
<img width=500px src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211986/7805163e-84bb-738e-7245-b59b4e3e8aa7.png>

この状態でSESのSMTP Endpointに対してメールを送信しても、以下のようなエラーが返り、送信に失敗します。
> Message rejected: Email address is not verified. The following identities failed the check in region US-EAST-1: yamada.taro@test1.com, Taro Yamada \<yamada.taro@test1.com\>

つまり「Domains」と「Email Addresses」に何も入っていない状態では、SMTP Endpoint宛にメールを送信してもRejectされますので、何も送信できないと言えます。

この検証から、SESのセキュリティはホワイトリスト方式であり、Fromのドメインやメールアドレスは必要なものだけを許可するというポリシーであることが分かります。

# 検証パターン

「目指したい姿」を実現するにはどう設定を投入すれば良いのかを、検証によって導くことを考えました。
今回、SESのセキュリティの設定の入れ方のパターンを全9パターン定義し、検証しました。

検証表と、検証の結果は以下の通りとなりました。
検証により、以下の**Case 7の組み合わせで各設定を投入すれば、「目指したい姿」が満たせる**と分かりました。

| No. | D | DKIM |EAs|IAM| P1 | P2 | P3 | P4 |
|:---:|:--|:----:|:--|:--|:--:|:--:|:--:|:--:|
| 1   | - | -    | - | - | F  | F  | F  | F  |
| 2   | ○ | F    | - | - | T  | T  | F  | F  |
| 3   | ○ | T    | - | - | T  | T  | F  | F  |
| 4   | ○ | F    | ○ | - | T  | T  | F  | F  |
| 5   | ○ | T    | ○ | - | T  | T  | F  | F  |
| 6   | ○ | F    | - | ○ | T  | F  | F  | F  |
| **7**   | **○** | **T**    | **-** | **○** | **T**  | **F**  | **F**  | **F**  |
| 8   | ○ | F    | ○ | ○ | T  | F  | F  | F  | 
| 9   | ○ | T    | ○ | ○ | T  | F  | F  | F  |

表の各項目については、以下の通りです。

* **D (Domains)** : Domainsにおける、唯一の許可ドメイン名 「test1.com」 (From) の登録有無 (○/-)
* **DKIM** : 上記の許可ドメイン名において、DKIMを有効にしたかどうか (T/F)
* **EAs (Email Addresses)** : Email Addressesにおける、許可メールアドレス 「yamada.taro@test1.com」 (From) の登録有無 (○/-)
* **IAM (ses:FromAddress)** : SMTP Credentialに紐づいているIAMポリシーのses:FromAddressにおいて、許可メールアドレス 「yamada.taro@test1.com」 (From) の登録有無 (○/-)
    * ses:FromAddressではFromのメールアドレスを制限可能。ses:FromAddressに書かれているメールアドレス以外がFromに指定されている場合、メールを送信できなくなります。
* **P1～P4** : 以下P1～P4のメール送信成否 (T/F)

上の表では、上記の7パターンにおいて、Fromを次の順で変更してメールを送信 (P1～P4) し、成否を確認し、結果を記入しました。

| No. | From Email Address    |
|:---:|:----------------------|
| P1  | yamada.taro@test1.com |
| P2  | sato.jiro@test1.com   |
| P3  | yamada.taro@test2.com |
| P4  | sato.jiro@test2.com   |

また、**DKIMがTの場合のみ、amazonses.com経由であることを隠蔽できる結果が得られています。**

# 検証結果から分かること

以下に、各設定について分かったことをまとめました。

## 1. DomainsはEmail Addressesよりも強い
Case 2とCase 4の結果から分かるように、Domainsで許可したドメインの具体的なFromメールアドレスをEmail Addressesに書いたとしても、許可はDomainsの範囲のままで、Email Addressesの範囲に絞られません。
これらの比較から、Domainsの設定はEmail Addressesの設定よりも強いことが分かります。
DomainsとEmail Addressesの和集合が、SESで送信を許可されるFromメールアドレスの範囲となります。

## 2. Domainsでドメインを許可すれば、Email Addressesのメールアドレス単位の許可は不要
1.で示したように、Domainsで許可したドメインのメールアドレスを、Email Addressesに書いたとしても、意味がありません。
例えば、Email Addressesの有無でケースが分かれるCase 7とCase 9は、結果が同じです。

Domainsでドメインを許可した場合は、Email Addressesでそのドメインのメールアドレスを改めて許可する必要がないと言えます。

## 3. Domainsはホワイトリストであり、ここに書かれたドメイン以外は通らない
Fromにtest2.comを指定し、ローカル部を変えて送信を検証 (P3とP4) しましたが、Domainsでtest1.comしか許可していない状況では、どちらも送信できません。
つまりDomainsでドメインを絞った時点で、他のドメインからの送信は全て遮断できると言えます。

## 4. amazonses.com経由を隠蔽するにはDKIMを設定する
検証結果から分かるように、DKIMのT/Fによってamazonses.com経由の表示の有無が変わりました。
amazonses.com経由であることを隠すために、DKIMを入れるべきだと言えます。

## 5. 「1人あたり1メールアドレス」にはses:FromAddressが必須
SMTP CredentialのIAMポリシーで「ses:FromAddress」を設定すると、Domainsで許可したドメインの中で、Fromのメールアドレスを限定できます。
この仕様を利用し、個人ごとにSMTP Credentialを与え、各SMTP CredentialのIAMポリシーでメールアドレスを限定すれば、「1人あたり1メールアドレス」を配って利用してもらうことができます。

設定例は以下となります。

```json:policy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "ses:SendRawEmail",
            "Resource": "*",
            "Condition": {
                "ForAnyValue:StringLike": {
                    "ses:FromAddress": [
                        "yamada.taro@test1.com"
                    ]
                }
            }
        }
    ]
}
```

# SESを使う時に押さえておきたい特徴や対策
分析結果から総合すると、以下のようにまとまりそうです。

* **ドメイン、メールアドレスのなりすましは基本的にできない**
    * SESでホワイトリストに追加する時に必ず所有を確認されるため
    * ただし**ドメインやメールアドレスそのものが乗っ取られないように注意**
        * DNSのセキュリティ、メールの認証情報 (SMTP Credential含む) の管理は厳重に
* **SESのアクセス許可はホワイトリスト**
    * Fromのドメインやメールアドレスを追加しない限りは、何もできない
    * SMTP Credentialの発行も必要
* **個人ごとにメールアドレスを発行するには、SMTP CredentialのIAMポリシーを設定**
    * Credentialごとにses:FromAddressでメールアドレスを縛る
    * 個人ごとにSMTP Credentialを払い出して配布する
* **個人の不正なメール利用を疑ったら、まずSMTP Credentialの無効化で対処**
    * 個人ごとにSMTP Credentialを分けていれば、この対処が可能
    * 上記で止まらない場合にDomainsやEmail Addressesを無効化
    * SMTP Credentialの使用状況はIAM Access Analyzerで追跡
* **DKIMでSES経由を隠蔽する**
    * 署名による信頼度アップ
    * DKIM自体がアクセス許可を左右するわけではない (検証結果からも分かる)
  
# 終わりに
ハードルが高そうなイメージがあるSESのセキュリティですが、それほど複雑なものではありません。
アクセス許可の観点でDomains/Email AddressesとSMTP CredentialのIAMポリシーを押さえ、メール送信の信頼度の観点でDKIMとSPF等を押さえれば、頭の中でも整理できると思います。
本記事が、SESを使ってメール送信環境を立てようと考えている皆様のお役に立てば幸いです。