送信ドメイン認証の仕組みを理解してSendGridで信頼性の高いメールを送りたい📨

# はじめに

2023年10月3日にGoogleとYahooが新しい迷惑メール判定ポリシーを発表したことを知ってますか？覚えてますか？

https://blog.google/products/gmail/gmail-security-authentication-spam-protection/

https://blog.postmaster.yahooinc.com/post/730172167494483968/more-secure-less-spam

こんにちは。私はレバテック開発部の基盤システムグループに属しているDヲタエンジニアです。いくつかのマイクロサービスを担当しており、そのうちの一つにメール送信を行うマイクロサービスがあります。

前述の発表が出た翌日の10月4日に私は本発表に関する記事をSlackに垂れ流していたのですが、何もせずに2023年が終わってしまいそうでした。そんな時、マイクロサービスから送られるメールが届かなくなる夢を見たので、2024年になる前に少しだけ影響を調べておくことにしました。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2811001/394b5261-47e4-bc9a-cffd-7c91be65d55c.png" width="500px">

下記画像はマイクロサービスから送られたメールの詳細をGmailのUIで見たものですが、メールが届かなくなる夢を見るよりも前からDMARCがFAILとなっている点が気になっていました。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2811001/f3689e4c-4500-3275-e98e-1d4925fa9b9c.png" width="500px">

そもそもDMARC認証がなんなのかよく知らなかったので今回はDMARC認証について調べたのですが、DMARC認証を知る上でSPF認証、DKIM認証、エンベロープFROM、ヘッダFROMを理解しておく必要があったので、それらを備忘録的に残しておきます。

また、メール送信を行うマイクロサービスではSendGridを使っているので、SendGridで送られるメールの送信ドメイン認証についても触れていきます。実際の認証の流れをdigコマンドを使って後追いしてみたりもしています。

# エンベロープFROM/ヘッダFROM についての理解を深める

知りたいことは全部Bardに教えてもらいました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2811001/87a49f79-d844-14b7-6892-e7be021c8a21.png)

> **エンベロープFROM**
>
> メールの送信元アドレスとして実際に使用されるアドレス。メールの送信や転送処理に使用される。メール本文のヘッダには表示されず、メールを受け取った側には表示されない。
>
> **ヘッダFROM**
>
> メールの送信元アドレスとしてメール本文のヘッダに表示されるアドレス。メールの送信や転送処理には使用されない。メールを受け取った側に表示される。
>
> **まとめ**
>
> エンベロープFROMとヘッダFROMは、メールの送信元アドレスを表すものである。しかし、両者は異なる役割を持ち、メールの送信や転送処理にはエンベロープFROMが、メールの表示にはヘッダFROMが使用される。
>
> エンベロープFROMは、メールの送信や転送処理に使用されるため、メールの配送に必要な情報を含む。そのため、なりすましメールの送信などに悪用される可能性がある。
>
> ヘッダFROMは、メールの表示に使用されるため、メールの送信元を偽装するために使用される可能性がある。
>
> メールの送信や転送に使用するアドレスと、メールの表示に使用するアドレスを使い分けることで、なりすましメールなどの被害を防ぐことができる。

# SPF/DKIM/DMARC についての理解を深める

こちらも全部Bardに教えてもらいました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2811001/ea00ab12-ba4d-6c85-0e94-2221d06a8377.png)

> **SPF**
>
> 送信元メールサーバのIPアドレスを認証する仕組み。ドメインのDNSレコードに、認証するIPアドレスを登録しておき、受信側のメールサーバがそのIPアドレスから送信されたメールかどうかを検証する。
>
> **DKIM**
>
> メールの署名を検証する仕組み。送信元メールサーバがメールに署名を付与しておき、受信側のメールサーバがその署名を検証することで、メールの送信元を認証する。
>
> **DMARC**
>
> SPFやDKIMの認証結果に基づいて、受信側のメールサーバがどのように処理するかを定義する仕組み。SPFやDKIMの認証に失敗したメールに対して、受信拒否や迷惑メールフォルダへの振り分けなど、適切な処理を行うことができる。
>
> **まとめ**
>
> SPF、DKIM、DMARCは、いずれもメールのなりすまし対策に有効な仕組みである。これらの仕組みを組み合わせることで、なりすましメールの検知率を高めることができる。

# SendGridのメール送信についての理解を深める

SendGridのブログの [SendGridからメール送信する場合のSPFとDKIMの認証の仕組み – 前編](https://sendgrid.kke.co.jp/blog/?p=10883) でしっかり説明されているので同じ事はわざわざ書きませんが、SendGridの設定次第でDNSの問い合わせ先が2通りに分かれるんだと認識しました。

1つは特に設定をしないでSendGridにお任せするパターンです。
エンベロープFROMは`sendgrid.net`になります。

![](https://sendgrid.kke.co.jp/blog/wp/wp-content/uploads/2019/02/02_via_sendgrid_before.png)

もう1つは、**Domain Authentication**という設定を行うパターンです。
画像ではエンベロープFROMは`em.kke.co.jp`になってますが、実際には自身で設定したドメインのサブドメインになります。
例えば`levtech.jp`なら`em7551.levtech.jp`になるようです。（サブドメインの部分はSendGridでがランダムに割り当てるっぽいです。）

![](https://sendgrid.kke.co.jp/blog/wp/wp-content/uploads/2019/02/03_via_sendgrid_after.png)

# 実際にメール送受信をして確認する
ここから先は「SendGridにお任せパターン」の状態で送信してみたメールがあるので、それを見ながら各認証の流れを追ってみます。
<sub>※levtech.jpのDNSの各レコードは何か見えざる力で設定されてる状態でした。なので本記事ではレコードの登録周りには触れません。</sub>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2811001/119e2340-80bf-bbdd-c468-7a951ba950e3.png)

## SPF認証

SPF認証に関係するヘッダを抜粋します。

```text:SPF関連ヘッダ抜粋
Return-Path: <bounces+20242900-74e6-watashino.address=leverages.jp@sendgrid.net>
Received: from o1.ptr2835.levtech.jp (o1.ptr2835.levtech.jp. [149.72.161.165])
        by mx.google.com with ESMTPS id d17-20020a05622a05d100b00423987299fdsi11801569qtb.665.2023.12.26.00.44.18
        for <watashino.address@leverages.jp>
        (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128);
        Tue, 26 Dec 2023 00:44:18 -0800 (PST)
Received-SPF: pass (google.com: domain of bounces+20242900-74e6-watashino.address=leverages.jp@sendgrid.net designates 149.72.161.165 as permitted sender) client-ip=149.72.161.165;
```

1. エンベロープFROMは`Return-Path`に書かれたドメイン`sendgrid.net`になります。
1. 送信元IPは`Received`に書かれた`149.72.161.165`になります。
1. `sendgrid.net`のSPFレコードを取得します。
    ```console
    ➜ dig +short sendgrid.net TXT | grep spf
    "v=spf1 ip4:167.89.0.0/17 ip4:208.117.48.0/20 ip4:50.31.32.0/19 ip4:198.37.144.0/20 ip4:198.21.0.0/21 ip4:192.254.112.0/20 ip4:168.245.0.0/17 ip4:149.72.0.0/16 ip4:159.183.0.0/16 include:ab.sendgrid.net ~all"
    ```
1. SPFレコードに`ip4:149.72.0.0/16`と書かれていて、`149.72.161.165`が含まれているので、SPF認証の結果はPASSとなります。

仮に**Domain Authentication**を設定した場合はエンベロープFROMが`em7551.levtech.jp`となります。
同じようにSPFレコードを取得すると以下が取得されますが、こちらも送信元IPと一致するのでSPF認証の結果はPASSとなります。

```console
➜ dig +short em7551.levtech.jp TXT
u9810091.wl092.sendgrid.net.
"v=spf1 ip4:149.72.161.165 -all"
```

**Domain Authentication**の設定に依らずSPF認証はPASSになりました。さすがSendGridですね。

## DKIM認証

DKIM認証に関係するヘッダを抜粋します。

```text:DKIM関連ヘッダ抜粋
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sendgrid.net; h=content-transfer-encoding:content-type:from:mime-version:subject:to: cc:content-type:from:subject:to; s=smtpapi; bh=bYMa22beQ1ReeuMWPzF4gWmybRvIKJZ74hmbkGiQyQY=; b=kssf0bIEdIZvvF/EalHbgmPo8/1r8vHbzVZQslmQDhkyV0aOKqjdLDKCfxMan7WQOLJi ToP3CPRD1Ivts6madl0PC4VkdOWKXAFOEAtAWxq+VMOIhcwQqFxOf9jNLWLDGr9kGsWbgz V0aQvUrMp+0YhQgdivaWis7+/STDg2QcY=
```

1. `DKIM-Signature`の`dタグ`の部分をみると`d=sendgrid.net`となっています。
1. `DKIM-Signature`の`sタグ`の部分をみると`s=smtpapi`となっています。
1. `[sタグ]._domainkey.[dタグ]`がレコードの取得先になります。
1. `smtpapi._domainkey.sendgrid.net`のTXTレコードを取得します。
    ```
    ➜ dig +short smtpapi._domainkey.sendgrid.net TXT
    "k=rsa; t=s; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDPtW5iwpXVPiH5FzJ7Nrl8USzuY9zqqzjE0D1r04xDN6qwziDnmgcFNNfMewVKN2D1O+2J9N14hRprzByFwfQW76yojh54Xu3uSbQ3JP0A7k8o8GutRF8zbFUA8n0ZH2y0cIEjMliXY4W4LwPA7m4q0ObmvSjhd63O9d8z1XkUBwIDAQAB"
    ```
1. 取得できた公開鍵で、`DKIM-Signature`の署名を検証します。
1. 署名の検証はしませんがおそらく有効と判断されると思います。

仮に**Domain Authentication**を設定した場合は、`DKIM-Signature`の内容が次のように変わります。

```diff:DKIM-Signatureのdタグやsタグが変わる
- DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sendgrid.net; h=content-transfer-encoding:content-type:from:mime-version:subject:to: cc:content-type:from:subject:to; s=smtpapi; bh=bYMa22beQ1ReeuMWPzF4gWmybRvIKJZ74hmbkGiQyQY=; b=kssf0bIEdIZvvF/EalHbgmPo8/1r8vHbzVZQslmQDhkyV0aOKqjdLDKCfxMan7WQOLJi ToP3CPRD1Ivts6madl0PC4VkdOWKXAFOEAtAWxq+VMOIhcwQqFxOf9jNLWLDGr9kGsWbgz V0aQvUrMp+0YhQgdivaWis7+/STDg2QcY=
+ DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=levtech.jp; h=content-transfer-encoding:content-type:from:mime-version:subject:to: cc:content-type:from:subject:to; s=s1; bh=bYMa22beQ1ReeuMWPzF4gWmybRvIKJZ74hmbkGiQyQY=; b=kssf0bIEdIZvvF/EalHbgmPo8/1r8vHbzVZQslmQDhkyV0aOKqjdLDKCfxMan7WQOLJi ToP3CPRD1Ivts6madl0PC4VkdOWKXAFOEAtAWxq+VMOIhcwQqFxOf9jNLWLDGr9kGsWbgz V0aQvUrMp+0YhQgdivaWis7+/STDg2QcY=
```

sタグ、dタグが変わっているためDNSに問い合わせるドメインは`s1._domainkey.levtech.jp`となります。

```
➜ dig +short s1._domainkey.levtech.jp TXT
s1.domainkey.u9810091.wl092.sendgrid.net.
"k=rsa; t=s; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnqAVlETyaI1rQJ09AeyK5DLtzbnMkwhQuAoQeVPvspEHq779EdsrTPnnU4cZ4s2/yWWxYl3rWl0mUQDVJlBBIY6hDjXYTLqaNCBCcHN8M0jlP/kyyfB8kZX93g3SvfRURj+lR052aYIjSei/gT1QV4uHa32jXOBzQKc7l7vfjcsQ9LmzVy6wyJqpW6MCuN5nYFMEt" "wABEm16i3TqSzHeXBKDnXWJEn02LKW/FcWWddUSFbNUy0JtnQTl5ej6iKT0pNEO8j14aEbK66yXemwB0/THGBcfTg24UCsmFvfh6xbccN0AyJdv+fkIkE19J2T+owFlRW3+jz255t3JYCRupwIDAQAB"
```

こちらも署名の検証はしませんがおそらく有効と判断されると思います。

**Domain Authentication**の設定に依らずDKIM認証はPASSになるようです。さすがSendGridですね。（2回目）

## DMARC認証

最後にDMARCを見ていきます。これは下記の結果の組み合わせで検証されるようです。

- SPF認証
- SPFアライメント
- DKIM認証
- DKIMアライメント

一瞬アライメントって何？って感じになりましたが、割と簡単な内容でした。

> **SPFアライメント**
>
> ヘッダFROMのアドレスが、SPFで認証したエンベロープFROMのドメインと一致するか。
>
> **DKIMアライメント**
>
> ヘッダFROMのアドレスが、DKIM署名のdタグのドメインと一致するか。

そしてこれらの項目とDMARC認証の関係は次のようになります。

```
DMARC認証 = (SPF認証 ∧ SPFアライメント) ∨ (DKIM認証 ∧ DKIMアライメント)
```

先ほどのメールでは下記のようになります。
- ヘッダFROMは`levtech.jp`。
- エンベロープFROMは`sendgrid.net`。
- dタグは`sendgrid.net`。

なので、

- SPFアライメントはFAIL。
- DKIMアライメントはFAIL。

ということで、DMARC認証はFAILとなります。

仮に**Domain Authentication**を設定した場合は、

- ヘッダFROMは`levtech.jp`。
- エンベロープFROMは`em7551.levtech.jp`。
- dタグは`levtech.jp`。

なので、

- SPFアライメントはFAIL。
- DKIMアライメントはPASS。

ということでDMARC認証はPASSとなります。

SPF認証、DKIM認証では**Domain Authentication**の設定に依らずPASSになリましたがDMARC認証は異なる結果になリました。

# おわりに

新しいポリシーの内容を読むとDMARC認証にも触れられているので、SendGridではちゃんと**Domain Authentication**を設定しないといけないなと認識することができました。また、SPF、DKIM、DMARC以外の内容もポリシーには含まれているので、こちらも再確認が必要そうです。