STARTTLS
概要
STARTTLS は、既に確立された平文の SMTP 接続を TLS 暗号化にアップグレードする拡張コマンドです。RFC 3207(2002年)で定義されており、RFC 2487(1999年)を置き換える形で発行されました。
メールサーバー間の通信は、もともと平文の SMTP(ポート 25)で行われていました。STARTTLS はこの既存インフラを活かしつつ暗号化を導入する手段として設計されています。接続の途中で TLS ハンドシェイクを挟み、以降の通信を暗号化します。
ただし STARTTLS は日和見暗号化(Opportunistic TLS)です。送信側サーバーが相手の STARTTLS 対応を検知できなかった場合、平文のまま送信を続行します。ネットワーク上の攻撃者が STARTTLS の能力通知を除去すれば、暗号化を迂回できます。この弱点を補うために MTA-STS(RFC 8461)や DANE(RFC 7672)が策定されました。
仕組み
STARTTLS は SMTP セッションの途中で TLS ハンドシェイクを挟み、暗号化チャネルを確立する方式であり、Implicit TLS とはアプローチが異なります。
SMTP セッションでの STARTTLS シーケンス
クライアント(送信側 MTA)がサーバー(受信側 MTA)に接続してから TLS が確立されるまでの流れは次の通りです。
- クライアントがポート 25(サーバー間転送)またはポート 587(メール送信)に TCP 接続する
- サーバーが
220 mail.example.com ESMTPで応答する - クライアントが
EHLO sender.example.comを送信する - サーバーが
250-STARTTLSを含む機能一覧を返す - クライアントが
STARTTLSコマンドを送信する - サーバーが
220 Ready to start TLSで応答する - 両者が TLS ハンドシェイクを実行し、暗号化チャネルが確立される
- クライアントが再度
EHLOを送信し、暗号化セッションでの機能一覧を取得する
ステップ 4 の応答に 250-STARTTLS が含まれていなければ、クライアントは TLS を開始できません。攻撃者がこの応答を改ざんして STARTTLS の行を除去するのが、STARTTLS ストリッピング攻撃です。
STARTTLS と Implicit TLS の違い
STARTTLS は平文接続を途中から暗号化にアップグレードする方式です。一方、Implicit TLS は接続の最初のバイトから TLS で通信を開始します。
RFC 8314(2018年)は、メール送信(Submission)において Implicit TLS(ポート 465)を STARTTLS(ポート 587)よりも推奨しています。Implicit TLS では平文のフェーズが存在しないため、ストリッピング攻撃が原理的に成立しません。
| 方式 | ポート | 暗号化開始 | ストリッピング耐性 |
|---|---|---|---|
| STARTTLS | 25, 587 | EHLO 後に切り替え | なし |
| Implicit TLS | 465 | 接続直後 | あり |
ただし、サーバー間のメール転送(MTA 間通信)ではポート 25 が標準であり、Implicit TLS への移行は進んでいません。サーバー間通信における STARTTLS の保護強化は MTA-STS と DANE が担います。
ダウングレード攻撃と対策
STARTTLS の最大の脆弱性は、暗号化の有無が平文のやり取りで決まる点にあります。攻撃者がネットワーク経路上に位置する場合、EHLO 応答から 250-STARTTLS を除去するだけで暗号化を無効化できます。送信側 MTA は相手が STARTTLS に対応していないと判断し、平文で送信を続行します。
この問題に対して 2 つの対策が標準化されています。
MTA-STS(RFC 8461、2018年) は、受信側ドメインが HTTPS 経由でポリシーファイルを公開し、「このドメインへのメール送信には TLS を必須とする」と宣言する仕組みです。送信側 MTA はポリシーをキャッシュし、TLS が確立できない場合はメール送信を保留します。ただし初回取得時は Trust On First Use(TOFU)に依存するため、最初の接続は保護されない可能性があります。
DANE(RFC 7672、2015年) は、DNSSEC で署名された TLSA レコードに正しい証明書情報を格納する方式です。送信側 MTA は DNS 問い合わせで証明書を検証するため、毎回の接続で保護が有効になります。TOFU の弱点はありませんが、DNSSEC の導入が前提です。現在 DNSSEC を利用しているドメインは全体の約 5% にとどまります。
TLS-RPT による障害検知
SMTP TLS Reporting(RFC 8460、2018年)は、TLS 接続の成功・失敗の統計を送信側から受信側に報告する仕組みです。_smtp._tls.example.com に TXT レコードを設定すると、送信側 MTA が TLS ネゴシエーションの結果を JSON 形式のレポートとして送ります。STARTTLS の障害や証明書エラーの検知に使います。
_smtp._tls.example.com. IN TXT "v=TLSRPTv1; rua=mailto:tlsrpt@example.com"
設定例
Postfix での STARTTLS 有効化と、MTA-STS ポリシーファイルの配置方法を示します。
Postfix での STARTTLS 有効化
送信側(クライアント側)の設定です。
smtp_tls_security_level = may
smtp_tls_loglevel = 1
may は日和見暗号化を意味します。相手サーバーが STARTTLS に対応していれば暗号化し、対応していなければ平文で送信します。
受信側(サーバー側)の設定です。
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.example.com/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/mail.example.com/privkey.pem
smtpd_tls_security_level = may
smtpd_tls_loglevel = 1
MTA-STS と組み合わせて TLS を強制する場合は、MTA-STS ポリシーファイルを HTTPS で公開します。Postfix 側で smtp_tls_security_level = dane にすると DANE 検証が有効になります。
MTA-STS ポリシーファイル
https://mta-sts.example.com/.well-known/mta-sts.txt に配置します。
version: STSv1
mode: enforce
mx: mail.example.com
max_age: 604800
mode: enforce は TLS 接続に失敗した場合にメール送信を行わないことを意味します。導入初期は mode: testing で TLS-RPT レポートを確認してから enforce に移行します。
DNS には次の TXT レコードを追加します。
_mta-sts.example.com. IN TXT "v=STSv1; id=20260411T000000"
id フィールドはポリシーの変更を検知するための識別子です。ポリシーを更新するたびに値を変更します。
確認方法
SMTP サーバーが STARTTLS に対応しているかは openssl s_client で確認できます。
openssl s_client -starttls smtp -connect mail.example.com:25
接続が成功すると、TLS バージョン、暗号スイート、サーバー証明書の情報が表示されます。SSL-Session セクションの Protocol が TLSv1.2 や TLSv1.3 であれば STARTTLS が正常に動作しています。
MX レコードを確認してからメールサーバーに接続するには、次の手順を使います。
dig MX example.com +short
10 mail.example.com.
openssl s_client -starttls smtp -connect mail.example.com:25 -servername mail.example.com
-servername オプションを指定すると、TLS ハンドシェイクで SNI(Server Name Indication)が送信されます。複数のドメインを 1 台のサーバーでホストしている場合に正しい証明書を取得するために必要です。
外部の視点からも確認したい場合は、Labee Dev Toolbox の Mail Auth API を使うと、外部の視点から見た結果を取得できます。
curl "https://labee.dev/api/mail-auth?domain=example.com"
レスポンスは次の形式で返ります。
{
"success": true,
"data": {
"spf": {
"record": "v=spf1 include:_spf.google.com ~all",
"exists": true
},
"dkim": {
"record": null,
"exists": false,
"selector": "default"
},
"dmarc": {
"record": "v=DMARC1; p=reject; rua=mailto:dmarc@example.com",
"exists": true
},
"bimi": {
"record": null,
"exists": false
}
},
"error": null,
"meta": { "responseTime": 120 }
}
data.spf.exists、data.dkim.exists、data.dmarc.exists の各フィールドで、メール認証レコードの公開状況を確認できます。STARTTLS の暗号化はトランスポート層の保護であり、SPF・DKIM・DMARC の送信ドメイン認証とは独立した仕組みです。両方を組み合わせることでメールのセキュリティが向上します。
よくある問題
STARTTLS の運用で発生しやすいトラブルと、その対処法をまとめます。
STARTTLS ストリッピングに気付かない
STARTTLS は日和見暗号化のため、暗号化に失敗しても平文でメールが届きます。送信側・受信側ともにエラーが発生しないため、暗号化が無効化されていることに気付きにくい構造です。TLS-RPT(RFC 8460)を設定すると、TLS 接続失敗の統計レポートが届くため異常を検知できます。
証明書の不一致でTLSハンドシェイクが失敗する
STARTTLS で提示される証明書のコモンネーム(CN)や SAN が MX レコードのホスト名と一致しない場合、厳密な検証を行う送信側 MTA は TLS 接続を拒否します。日和見暗号化モード(may)では証明書検証をスキップして接続を継続しますが、MTA-STS の enforce モードや DANE では接続が失敗し、メールが送信保留になります。MX レコードで指定しているホスト名と証明書の SAN を一致させます。
自己署名証明書を使い続ける
日和見暗号化モードでは自己署名証明書でも TLS 接続は成立します。しかし MTA-STS は CA が発行した証明書を要求するため、自己署名証明書のままでは MTA-STS を導入できません。Let’s Encrypt などの無料 CA で証明書を取得して置き換えます。
MTA-STS の mode: testing から移行しない
mode: testing はポリシー違反を検知してもメール送信をブロックしません。TLS-RPT レポートで問題がないことを確認したら mode: enforce に変更します。testing のまま放置すると、ダウングレード攻撃への対策が実質的に機能しません。
ポート 465 と 587 を混同する
ポート 587 は STARTTLS で暗号化をアップグレードするメール送信(Submission)用ポートです。ポート 465 は Implicit TLS で最初から暗号化されたメール送信用ポートです。メールクライアントの設定で誤ったポートと暗号化方式を組み合わせると接続に失敗します。ポート 587 なら暗号化方式に STARTTLS を、ポート 465 なら SSL/TLS を選択します。