電子メール送信に関する技術
ふと気になって調べたことの備忘メモです ✍
(2022/4/2追記)Twitterやはてブで色々とご指摘やコメントを頂いたので、それに基づいて加筆と修正をおこないました
特に、幾つかの技術については完全に誤った説明をしてしまっており、大変助かりました…ありがとうございました🙏
なぜ調べたか
メール送信機能のあるWebアプリケーションを開発・運用していると、 特定のアドレスに対してメールが届かないんだが
とか MAILER-DAEMONなるアドレスからメールが来たんだけど
といった問い合わせを受けて原因を探ることになります
実務においては、Amazon SES や SendGrid といったメール送信処理を抽象的に扱えるサービスを使うことが多いと思いますが、
ことトラブルシューティングにおいては、その裏にある各種技術についての概要を知っていると、状況把握や原因特定をしやすくなります
ありがたいことに、電子メールは枯れた技術のようで公開日が古い記事でも参考になるものがたくさんありますが、
現代に全体感を理解しようとして調べていくと、歴史的経緯からアドホックに定義された(ように見える)仕様が色々とあってなんども調べ直すことがあって無駄を感じたので、まとめることにしました
なお、掲題の通りではありますがこの記事はメール送信に関するもののため、メール受信やメール内容に関連する各種技術については触れていません
加えて私は一介のWebアプリケーション開発者で雰囲気で電子メールを使っている身なので、誤りが多分に含まれる可能性があります
あしからず 🦵
調べたこと
全体感を捉えるために、調べるとよく出てくる各要素技術についてどこら辺に存在するものかわかるよう図に起こしてみました 🌏
以下、メールの送り元からメールの送り先に至るまで順に見ていきます
MUA / MSA / MTA
電子メールの送信をざっくり理解するにあたっては、大きく3つの登場人物を意識するとよい
記事によってMSAとMTAが同質化していたりなど差異がある場合もあるが、雰囲気で理解しておけば充分の模様
- MUA (Message User Agent)
- メールを送信(ないし受信)するソフトウェア
- 元々はクライアントPCで動作するメールソフト(Outlookなど)のような、SMTP / IMAPによる通信をおこなうソフト群を指していたものと思う
- Amazon SESのようなAPIによるメール送信が可能な仕組みを利用する場合は、クライアントのプログラムも広義のMUAと言えるかも?
- MSA (Message Submission Agent)
- MUAからのメール送信を受け付けるソフトウェア
- 受け付けたメールをMTAへリレーしていく
- メールの送信元にあたり、AWSアカウント上に設定したAmazon SESやEC2上で動作する Postfix などがこれに該当する
- 不正なユーザーによるメール送信をさせないよう、 MUA に対して何かしらの認証の仕組みが必要 になる
- MUAからのメール送信を受け付けるソフトウェア
- MTA (Message Transfer Agent)
- メールを目的のサーバまで転送していくソフトウェア
- MSAやMTAから送られてきたメールを宛先のMTAまで転送していく
- MSAがMTAの役割も兼用することも多い
- 基本的に
MUA -> MSA -> (状況によって宛先に至るまで別のMTAを経由する) -> 宛先のMTA
という経路でメールが渡されていく
- 基本的に
-
電子メールはパブリックかつ性善説的な仕組み なので、あるMTA(ないしMSA)から別のMTAに対して自由にメールを転送することができる
- メールアドレスを知っていれば、その人に対していつでもメールを送れますよね
- このため、 メール転送元が妥当なサーバーであるか検証するための仕組みが必要 になる
SMTP
メール送信には SMTP (Simple Mail Transfer Protocol)
というプロトコルが使われる
今回は詳しく触れないが、メール受信には IMAP (Internet Message Access Protocol)
などの別のプロトコルが使われるので、これらを混同しないように注意する
SMTPではHTTPと同じくASCII文字によりコマンドを組み立ててTCP通信でやり取りをおこなう
このため、サーバへのアクセスが可能であればtelnetコマンド等を使ってメール送信をおこなうことができる
サブミッションポート
前述の記事でも触れられているが、宛先サーバに対して接続する際に利用するポート番号として 587
または 465
が一般的であり、サブミッションポートと呼ばれる
SMTPで利用されるポート番号と言えば 25
が有名だと思うが、こちらはMTAがメール転送をおこなうために使われている
(なお、25番ポートについてサブミッションポートのような一般的な呼称はないようだが、 スタンダードポート と表現しているケースがあった)
理由としては、元々 SMTPが認証を必要とせず誰に対しても自由にメールが送れたことから、スパマー(スパムメール を送ってくる人のこと)が跋扈するようになり、
これに対処するために、MSAとMTAで利用するポートを分けた上で、サブミッションポートでは SMTP AUTH 等によってMSA利用時に認証をおこなった上で、25番ポートにアクセスしてくる送信元MTAの信頼性(スパマーでないか)を検証することで安全なメール送信を実現している
SMTPS
現在ではメールを送信するクライアントとしてはGmailなどのWebアプリケーションやAWS SDKといったものが増えてきているが、元々MUAはOutlookやThubderbirdといった、SMTPによるやり取りが可能なソフトウェアをクライアントにインストールして利用していた
こちらについては 465
を用いてコネクション接続の段階から暗号化通信をおこなう SMTPS(SMTP over SSL/TLS)
という規格が存在する
本ポートについては、歴史的経緯から一時的に非推奨になっていたことがあった経緯から利用を推奨しないとする情報も存在するが、RFC8314において(別プロトコルとの重複利用になっているようだが)有効なポートとされている
また、実体として現在も一般的に利用されており、例えばBIGLOBEのメールサーバー接続設定では465ポートを利用してMUAからMSAに接続するようユーザに要求していた
STARTTLS
前述したSMTPSでは、そのポートを利用開始した段階で暗号化通信が強制されるため通信経路の安全性が高くなるが、
SMTPは元々平文で送受信をおこなう仕様であったことから、宛先MTAが古いものであった場合に暗号化通信に対応していない可能性があり、そうするとメールを正しく送ることができないリスクが発生する
この問題に対処するために、SMTPにおいては同一ポートを利用する代わりに、まず平文でやり取りを開始し、相互がTLSに対応していることを確認してから途中で暗号化通信に切り替える…という動作をさせることができる
これは、SMTP通信中にSTARTTLSコマンドを発行することで実施することができ、
サブミッションポートとして 587
を利用する場合と、 25
ポートにおいて暗号化通信を利用する場合に用いられる
なお、STARTTLSのような接続の過程で接続対象に応じて暗号化方式を変更する仕様のものを日和見暗号化方式と呼び、メッセージ送信の過程で様々なMTAを経由する可能性があるSMTPにおいては実利上の優位性がある
特にSSLやTLSに関する文脈では、特に Implicit TLS / Explicit TLS
という語で説明される場合もある
SMTPSはImplicit TLS 、STARTTLSはExplicit TLSにあたる
なお、Googleが送受信しているメールについては、全体のトラフィックに対してどの程度の割合が暗号化された経路でやり取りされているか確認できる
2022年時点では、おおよそ85%程度のメールが暗号化された経路で送信されているらしい
https://transparencyreport.google.com/safer-email/overview より引用
OP25B
先ほど、 電子メールは性善説的な仕組み
と説明したが、当初スパマーは本人認証をおこなう必要があるMSA経由でのメール送信をおこなわず、自身で野良MTAを建てて25番ポートに対してボット等によりスパムメールやウィルスメールを送りつける…という手法を取っていた
これに対して対抗する手段として、ISP(Internet Service Provider) 各社によって 契約ユーザーから 25 番ポートを利用しての外部接続を遮断する という措置を取ることで、25番ポートへのアクセスをISPが運用するメールサーバやメール送信事業者に限定することで不審な送信元を遮断している
参考:AWS を使った場合のメール送信
Amazon SESを例に取ると、APIまたはSMTPによるメール送信を選択できるが、いずれのケースにおいてもマネージドのMSA(図中の SES
)が利用される
これによって、AWS外部のMTA(図中の Receivers
)から見た時に、メールの送信元(図中の SES
)は充分に信頼のおける相手であり、かつメール送信者(図中の Sender
)は送信元により認証がおこなわれているため、スパムやウィルスメールを送ると送信元にばれてしまうこととなる
https://docs.aws.amazon.com/ja_jp/ses/latest/dg/send-email-concepts-process.html より引用
また、EC2等を用いて自身でPostfixを運用すれば制限を迂回できるかというとそのようにはなっておらず、
AWSアカウントから25番ポートでのアウトバウンド通信はデフォルトで許可されておらず、利用したい場合はAWSに対して制限解除申請をおこなう必要がある
SMTP リレー
以上に説明したような背景から、MTAは自身で運用するよりもメール送信を専門にしている事業者のものを使った方が受信者側から見た時に信頼度が高く、メリットが大きい
前述のAmazon SESやSendgGridは、メール送信元として自社の運用しているサーバを透過的に使わせるSMTPリレーに対応しており、社内ネットワーク等で独自構築したMSAからメール送信の部分のみを委譲するユースケースを想定している
こうしたSMTPリレーサービスを専門とする業者も調べると多数出てくる
MTAサーバー間におけるSMTPリレーは25番ポート & STARTTLSを使った暗号化通信によっておこなわれる
メールが配送されていくにあたってどのようなSMTPサーバを経由したかについては、メールヘッダーを取得した後に、以下のサイトで確認できる
例えば以下の表示であった場合、メールは mail7.nyi.meetup.com
→ COL004-MC1F51.hotmail.com
→ COL004-OMC4S14.hotmail.com
→ mx.google.com
といったMTAを経由して送信されたことがわかる
https://toolbox.googleapps.com/apps/messageheader/ より引用
DNS
MTA間でのメール送信の際には メール送信元/転送元の妥当性検証 が重要であることを説明したが、これには DNS(Domain Name System)
が重要な役割を占めている
メールアドレスは hogehoge@example.com
のような形式で表記されるが、この内アットマークより後ろのドメイン部 example.com
に対応するIPアドレスをDNSにより解決することでメール送信先を特定する
CLIからDNSへアクセスする場合はdigコマンドを利用する
以下、特にメール送信に関連するレコードについて詳細を見ていく
MX レコード
MSAから宛先のMTAに対してメールを転送していく過程では、宛先ドメインのMX(Mail Exchange)レコード
が参照される
送信元ドメインのMXレコードは、メール送信においては不要(関係がない)ことに留意する
例えば、 gmail.com
ドメインのMXレコードを調査すると以下が返却され、gmail-smtp-in.l.google.com
にアクセスすれば宛先のMTAと通信できることがわかる(実際にはOP25Bにより試せないため推測ですが…)
$ dig gmail.com MX
略
;; ANSWER SECTION:
gmail.com. 2159 IN MX 10 alt1.gmail-smtp-in.l.google.com.
gmail.com. 2159 IN MX 40 alt4.gmail-smtp-in.l.google.com.
gmail.com. 2159 IN MX 20 alt2.gmail-smtp-in.l.google.com.
gmail.com. 2159 IN MX 5 gmail-smtp-in.l.google.com.
gmail.com. 2159 IN MX 30 alt3.gmail-smtp-in.l.google.com.
略
MXレコードが存在しない場合は、フォールバックとして AレコードまたはAAAAレコード
が示すアドレスに対して接続を試みるらしい
したがって、トラブルシューティング等にあたってMXレコードが無いからといって即座にメールを受信できないドメインと断定することはできない
MTA-STS
前項で触れたように、STARTTLSは接続対象とのネゴシエーションの過程で暗号化レベルが変化することからダウングレード攻撃のような、不正な第三者が平文での通信を強制させる操作に対して脆弱性がある
こうした状況に対処するため、2018年にRFC8461 MTA-STS (SMTP MTA Strict Transport Security)
という新しい仕様が登場している
MTA-STSは、受信MTA側がメールの送信元に対して暗号化接続を強制させるといったポリシーを明示するもので、送信元が暗号化に対応できない場合メールが送信されなくなる
例えばGmailはMTA-STSに対応しており、自身で利用しているドメインで使いたい場合は以下手順で設定ができる
既存技術も踏まえたMTA-STSに対する説明としては、以下の記事で丁寧に解説されている
SPF / DKIM / DMARC
今までは、 MUA -> MSA -> MTA
とメールが転送されていく過程で利用される技術について説明してきたが、
ここからは宛先MTAが送信されてきたメールを受け付ける過程でおこなわれる妥当性検証について説明していく
検証はいずれもDNSに登録された幾つかのレコードと、SMTP通信中に宛先MTAに提示された情報を比較することでおこなわれる
簡単なまとめは以下の通り
- SPF
- メール送信元ドメインの妥当性検証
- DKIM
- メール内容の改竄検知
- DMARC
- メール送信元ドメインの詐称の抑止
- 不審なメールを検知した際の扱い方の定義
ちなみに、それぞれの設定はいずれもオプションのため、設定をしなくてもメール送信は可能である
SPF
送信元ドメインの妥当性検証は、元々の送信元IPアドレスがメールの送信元ドメインに紐づくものか確認する
例えば、 hoge@example.com
がFromアドレスとして記録されたメールがIPアドレス xxx.xxx.xxx.xxx
から送信されてきた場合、 example.com
からのメール送信元として xxx.xxx.xxx.xxx
が妥当なものであることが導出できればよい
この一連の仕組みを SPF(Sender Policy Framework)
と呼び、DNSに所定の形式のTXTレコードを登録することで実現する
Amazon SESにおいては、以下のように amazonses.com
を送信元として許可する設定にすればSPFの設定が完了していることになる
example.com
ドメインにSESを有効な送信元として紐付けている場合、digコマンドで問い合わせると以下のような値が返却される
DNSにレコードを登録できるのはそのドメインを所有している人物に限られるため、これによって example.com のメール送信元として amazonses.com を認めている ということが確認できる
これに加えて、 amazonses.com
に対してAレコードの問い合わせをおこなった際に前述のIPアドレス xxx.xxx.xxx.xxx
が返却されれば、 example.com のメール送信元として IP アドレス xxx.xxx.xxx.xxx が適切である ことがわかる
詳細な仕様は以下が詳しい
$ dig example.com TXT
略
;; ANSWER SECTION:
example.com. 86400 IN TXT "v=spf1 include:amazonses.com ~all"
略
DKIM
SPFによるチェックによりメールアドレスの送信元ドメインの妥当性がわかるが、この情報はメールヘッダに設定された値を参照するため、メールが転送中にMTA等により改竄されていないことを前提としている
以下図のヘッダFromが不正に書き換えられた際に検知できる必要がある
https://sendgrid.kke.co.jp/blog/?p=10121 より引用
これについては、 DKIM(DomainKeys Identified Mail)
という技術を使って実現する
DKIMはメールに対して公開鍵暗号方式にて電子署名を付与し、その公開鍵をDNSに selector._domainkey.example.com
の形式で登録することで、宛先MTA側にて改竄検知を可能にする
Amazon SESにおいては、以下手順によりDKIMレコードを設定できる
コンソール等から操作するだけで電子署名の作成および管理をマネージドに実施してくれるので簡単に利用できる
宛先MTA側からDKIMレコードを検証するフローとしては以下のようになる
Gmail等で受信したメールのヘッダ情報を確認し、 DKIM-Signature
ヘッダの s=hogehoge;
と書かれている箇所を参照する
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/simple; s=hogehoge; d=example.com; 後略
digコマンドによりTXTレコードを引いた時に、 p=xxxxxx/yyyyyy/zzzzzz
として値が返却される値が公開鍵として利用される
$ dig hogehoge._domainkey.example.com TXT
略
;; ANSWER SECTION:
hogehoge._domainkey.example.com. 7192 IN CNAME hogehoge.dkim.amazonses.com.
hogehoge.dkim.amazonses.com. 3570 IN TXT "p=xxxxxx/yyyyyy/zzzzzz"
略
詳細な仕様は以下が詳しい
DMARC
SPFやDKIMによって送信元ドメインとメール内容の検証ができたが、これらの検証で不適格となったメールの取扱については規定されておらず受信者側に任されている
これについて、悪意のある第三者によって不正に送信されたメールの取扱方法を正当な送信元から表明するための仕組みが考案され、 DMARC(Domain-based Message Authentication, Reporting and Conformance)
として運用されている
内容は、送信元ドメインに対して _dmarc
をつけたものでDNSにTXTレコードを問い合わせることで確認できる
$ dig _dmarc.example.com TXT
略
;; ANSWER SECTION:
_dmarc.example.com. 86400 IN TXT "v=DMARC1; p=quarantine; rua=mailto:system@example.com; ruf=mailto:system@example.com; rf=afrf; pct=100"
略
各値の詳細は以下を参照するとよい
上記サンプルであれば、 認証に失敗した全てのメールについて、迷惑メールに振り分けた上で認証が失敗したことを system@example.com 宛に報告する
というような意味合いになる
Amazon SESにおける設定方法は以下
参考: Amazon SES + Terraform を利用している場合の SPF / DKIM / DMARC 設定例
Terraformで設定する例を以下記事でまとめているのでよければどうぞ 🍢
Bounce / Complaint
メールが認証上の諸問題をクリアできる状態で送信された場合でも、受信側の事情によりメールが配送されない場合があり、以下のような分類がある
特にハードバウンスや苦情のケースにおいては、送信者側としてはこれ以上宛先に対してメールを送るべきでない状態であるため、何かしらの方法を使って対象のメールアドレスに対するメール送信を抑止する必要がある
- バウンス(Bounce)
- ソフトバウンス
- 宛先のメールサーバ上のメールボックスが一時的に容量超過でメールを受け取れなかった、サーバがダウンしている…など、 一時的な理由 によりバウンスされた場合
- ハードバウンス
- 宛先のメールアドレスが存在しなかった、サーバとして受信拒否されている…など、 恒久的な理由 によりバウンスされた場合
- ソフトバウンス
- 苦情(Complaint)
- メールが受信された後、ユーザによって迷惑メールとしてマークされた場合
上述したようなメールアドレス等に対する送達性に起因するバウンス以外にも、SpamAssassin などのスパムメールフィルタによってバウンスが発生する場合もある
Envelope-From / Return-Path
前述した様々な理由によって受信者側にてバウンスや苦情が発生した際にメールの送信元へ通知する必要がある
この時に用いられるメールアドレスとして、メールの送信元サーバーを示す Envelope-From
が利用される
これは、MUAがメールの送信依頼をおこなったアドレスと、MSAによって配送される際のアドレスが変わりうるため区別されている
Envelope-Fromに対して、メールの送信依頼元のアドレスは Header-From
と言われる
Amazon SESにおいては、 xxxxxx-yyyyyy-zzzzzz@ap-northeast-1.amazonses.com
のような、メール毎に採番された一意のIDを含むアドレスが Envelope-From
に指定される仕様となっており、自動的に指定された上で受信内容をAWS側で解釈してくれる
詳細な仕様としては以下のような形式でEメールがやり取りされる
バウンスが発生すると、受信MTAは Envelope-From
に記録されていたアドレスを Return-path
ヘッダとしてメールに追記するため、
送信元にてバウンスしたメールの Return-Path
を確認することで、受信MTAからどのアドレスを経由してバウンスメールが届いたか確認できる
一連の動作から Return-Path
には Envelope-From
と同じアドレスが設定されることになるが、 それぞれの情報を送信側 / 受信側のどちらが書くかは留意したい
レピュテーション
あるドメインについてバウンスや苦情がどの程度発生しているかは、与信管理のような仕組みが各メール配信事業者によって運用されており、レピュテーションと呼ばれる
メール送信者にはこれらの発生率を低く保つことが要求される
自身が利用しているドメインのレピュテーションは、以下の記事で挙げられたようなサイトで確認できる
Amazon SESにおいては、メールに対してバウンスや苦情が発生した際にSNSにて通知を受けることができる
また、バウンス率や苦情率といった統計情報もドメインごとに計算されており、ユーザにて確認ができる
バウンス率・苦情率が一定値を超過した場合、SESの利用制限を課せられる場合がある
サプレッションリスト
バウンスメールが発生した場合の対処としては、対象のアドレスをメール送信者側に構築されたサプレッションリストに対して登録するのが一般的である
Amazon SESにおけるサプレッションリストはアカウントレベルのものとAWS全体のものがあり、詳細は以下
MAILER-DAEMON / Mail Delivery Subsystem
バウンスメールがReturn-Pathに対して返送される過程で、送信元のメールアドレスに対して MAILER-DAEMON
や Mail Delivery Subsystem
といった送信者からメールが送られて来る場合がある
これらはバウンスが発生したことをメール送信者に対して通知するためのもので、内容を確認するとバウンスが発生した詳細な理由が確認できる
バウンスの詳細な理由は、SMTP ステータスコードに加えて各MTAにて独自拡張された形式で説明されるため、理解するのが難しい場合がある
以下のサイトがとても詳しいので参考になる
おわりに
かなり疲れましたが、勉強になったのでよかったです🍨
誤りや、他に紹介したほうがよさそうな内容があればぜひ教えてください!
Discussion
俺もAmazonのパーソナル・ドキュメントサービスを利用したくてDebian LinuxへMSAをセットアップした。
まる2日も掛かったのはメールサーバーの仕組みが解ってなかったため。
わかっていれば1時間で済む話だった。
このサービスはMSAを使ってKindle Paperwhiteに紐付いたメールアドレスへhtmlファイルが添付した空のメールを飛ばせばKindle本として読めるというサービス。Documentを読むには超役立つサービスなのだ。
ただ、imgタグのsrcにはbase64エンコードした画像データを記述しないといけないのが困った所。
使ったMSAはExim4で、MTAにはGmailを使った。ハマりポイントはGmailとのSMTP AUTHの際にGmailがApplication-Specific Passwordを要求してきたこと(2022/03現在)。
Exim Internet Mailer
Googleは2段階認証を採用したユーザーにはこれを発行してくれるのだ。
Googleにログインする際につかうPasswordがSMTP AUTHにも使えるのだと思い込んでたら足をすくわれた。
おまけに、下記のWikiの通りにやっても上手く行かず絶望しかけた。
Exim - Debian Wiki
Exim4はPostfixとは異なりCyrus SASL(Simple Authentication and Security Layer)も同時インストールせずとも単独でGmailとのSMTP AUTHが成功した。
Googleは自社製品、たとえばGMailアプリ以外からのSMTP AUTHをする際にはApplication-Specific Passwordを要求するようだ。試しにmacOSに標準で入ってるメールアプリでGmailを利用しようとすると同じくApplication-Specific Passwordを与えないとメールが読めなかった。
Application-Specific PasswordはGitHubではAccess Tokenと呼ばれている。