理系学生日記

おまえはいつまで学生気分なのか

送信ドメイン認証としてのSPFとDKIM

最近はなりすましメール周りについて調べていまして、いままでまったくタッチしてこなかったメール周辺知識を必死で吸収するようにしています。

メールにおける送信者情報

メールにおいては、2つの送信者情報が存在します。

  • エンベロープFrom
  • ヘッダFrom

エンベロープFrom

ご存知の通り、メールの配送プロトコルはSMTPです。 SMTPは配送を行うプロトコルであるがゆえに、当然送信者や宛先のメールアドレスが必要になります。

これらは、MAILRCPTといったSMTPのコマンドにより、メールサーバ(MTA)間でやりとりされます。 このうち、MAIL FROMでやりとりされる情報は「reverse-パス」という名前がついていますが、これが俗にいう「エンベロープFrom」です。

The first step in the procedure is the MAIL command.

MAIL FROM:<reverse-path> [SP <mail-parameters> ] <CRLF>

RFC 5321: 3.3. Mail Transactions

あくまで「配送」に使われる情報であるため、メールの受信者にはこの情報は届きません。

ヘッダFrom

一方で、メール本体をメール本文とともに構成するのがメールヘッダであり、ここにも送信者情報が含まれます。

from = "From:" mailbox-list CRLF

ender = "Sender:" mailbox CRLF

reply-to = "Reply-To:" address-list CRLF

RFC 5322: 3.6.2. Originator Fields

メールソフト(MUA)がユーザに「送信者」として表示するのは、このうちのFromヘッダの情報であることが多いです。

送信ドメイン認証

上記のように、メールには2種類の送信者情報が存在しますが、それが正しく名乗られているかを確認する仕組みは長らくありませんでした。 これを何とかしようという技術が、一般に「送信ドメイン認証」と呼ばれる技術になります。

SPF (Sender Policy Framework)

エンベロープFromの詐称を検知しようというのが、SPF (Sender Policy Framework)です。 あるメールを受信した時、その送信者が「ドメイン所有者が認めたメール送信者であること」をチェックします。 現状のRFCはRFC 7208になります。

原理は単純で、メールを受信したMTAは、reverse-パスのドメインのDNSサーバに対して、SPFレコードを問い合わせます。 SPFレコードには、当該ドメインのメールは「どこから送信されるのか」という情報が記載されており、ここで宣言された場所からメールを受信したか否かによって認証を実施します。

SPFの優れたところは、SPF対応が送信者側のDNSサーバへTXTレコードを追加することだけで完了する点です。

例えば、qiita.comが「どこからメールを送信すると宣言しているのか」を見てみましょう。これはqiita.comのTXTレコードを問い合わせてみるとわかります。

$ dig +short -t TXT qiita.com | grep v=spf1
"v=spf1 include:_spf.google.com include:amazonses.com include:servers.mcsv.net include:mail.zendesk.com include:aspmx.pardot.com a:qiita.com ~all"

includeは「どこからメールを送るかの設定は別のドメインの設定を見てくれ」という設定です。

「qiita.com」を名乗るメールは、GoogleやAmazon SES、Zendeskなどから送られると宣言されています。 例えば、include:amazonses.comという記述からは、Qiitaが「Amazon SESからqiita.comを名乗るメールを送信する」と宣言していることがわかります。

具体的に「どこから」ということを確認するには、amazonses.comへ再度TXTレコードを問い合わせれば良いです。

$ dig +short -t TXT amazonses.com | grep v=spf1
"v=spf1 ip4:199.255.192.0/22 ip4:199.127.232.0/22 ip4:54.240.0.0/18 ip4:69.169.224.0/20 ip4:23.249.208.0/20 ip4:23.251.224.0/19 ip4:76.223.176.0/20 ip4:54.240.64.0/19 ip4:54.240.96.0/19 ip4:52.82.172.0/22 -all"

ここからは、Amazon SESのメールの送信元となるIPアドレスがip4 mechanismを使って列挙されており、-allによってそれ以外の送信元は「認証していない」ことがわかります。 このように、include mechanismを使うことにより、メール配信サービスを利用してもSPFに対応可能になっています。

qiita.comのTXTレコードに戻ると、a:qiita.comは、qiita.comのDNS Aレコード/AAAAレコードが示すIPアドレスからの送信も「認証する」ことを示します。 最後の~allはSoftfailを示しており、それ以外の送信元からのメールは「認証はしないがリジェクトまではするな」、という微妙な意味合いになります。

A "softfail" result ought to be treated as somewhere between "fail" and "neutral"/"none". The ADMD believes the host is not authorized but is not willing to make a strong policy statement. Receiving software SHOULD NOT reject the message based solely on this result, but MAY subject the message to closer scrutiny than normal.

RFC 7208: 8.5. Softfail

参考文献

DKIM (Domainkeys Identified Mail)

SPFが送信元の「場所」を認証するのに対し、DKIM (Domainkeys Identified Mail)は電子署名によって送信元を認証します。

シーケンスはシンプルで、まずメールの送信側では電子メールに(秘密鍵を利用して)電子署名を付与します。 メール受信側は、送信元のドメインに対して(秘密鍵に対応する)公開鍵をDNSサーバに要求し、手に入れた公開鍵で署名を検証します。 送信元ドメインが詐称されていた場合、詐称者は当該ドメインの「秘密鍵」を持っていないため、署名の検証には失敗するという筋書きです。

では、Qiitaの公開鍵を探してみましょう。

Qiitaから私に届いたメールにも当然DKIMの電子署名が付与されています。具体的には、メールヘッダのうちのDKIM-Signatureです。

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qiita.com; s=k1; t=1657872560; x=1658174960; i=info@qiita.com; bh=US1Q2GWWPmZVgAxFONV7ApkDHyo9G4FRGLtg2Y1Ea+s=; h=Subject:From:Reply-To:To:Date:Message-ID:X-MC-User:Feedback-ID:List-ID:List-Unsubscribe:List-Unsubscribe-Post:Content-Type:MIME-Version:CC:Date:Subject:From; b=T4JMm6GzMhXBKAKVf+4jcJWmtvKoGp8jbskn0+CctgukAxfUYW8QwKzEOx0emJN3k42iVDnQ245Hbul4voO4BI4V69t4e7GXPUteW31qHMf8G9niBqlzBky6cTvJ47ys/Si/yqUPc2+cGA3Fu59YQMfz3iMZb/CXGImeUJO0/YM=

ここにはさまざまな情報がありますが、ヘッダの内容はRFC 6376の3.5. The DKIM-Signature Header Fieldに定義があります。 d=qiita.comがドメイン、s=k1がセレクタを意味しています。 DKIMの仕様においては、<セレクタ>._domainkey.<ドメイン>に公開鍵を配置することになっているため、これを理解していれば公開鍵が取得可能です。

$ dig +noall +answer -t TXT k1._domainkey.qiita.com
k1._domainkey.qiita.com. 151    IN      CNAME   dkim.mcsv.net.
dkim.mcsv.net.          20971   IN      TXT     "k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDbNrX2cY/GUKIFx2G/1I00ftdAj713WP9AQ1xir85i89sA2guU0ta4UX1Xzm06XIU6iBP41VwmPwBGRNofhBVR+e6WHUoNyIR4Bn84LVcfZE20rmDeXQblIupNWBqLXM1Q+VieI/eZu/7k9/vOkLSaQQdml4Cv8lb3PcnluMVIhQIDAQAB;"

dkim.mcsv.net側に公開鍵が登録されており、その値はBase64でMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDbNrX2cY/GUKIFx2G/1I00ftdAj713WP9AQ1xir85i89sA2guU0ta4UX1Xzm06XIU6iBP41VwmPwBGRNofhBVR+e6WHUoNyIR4Bn84LVcfZE20rmDeXQblIupNWBqLXM1Q+VieI/eZu/7k9/vOkLSaQQdml4Cv8lb3PcnluMVIhQIDAQABであることがわかります。

今後

送信元ドメイン認証としては、あとDMARCの理解(RFC 7489)とAuthentication-Results header(RFC 7601)の理解が必要そうです。 ただ、ちょっと読みきれなかったので、まずはここまで。