ドメイン健康診断 は、ドメイン名を入れると、メールの認証と DNS の基盤にかかわる 10 項目を判定し、直すための DNS レコードの例を示すツールである。項目ごとに「良好」「注意」「問題」「未設定」「確認できず」のどれかを付け、全体をまとめた点数は出さない。
先に公開した DMARC チェッカーが見ていたのは、SPF、DKIM、DMARC の三つだった。なりすましメールを防ぐ設定はこの三つが中心になる。ただ、メールが届くか、途中で盗み見られないか、ドメインの名前がそもそも引けるかは、別のレコードで決まる。受信サーバーを示す MX が壊れていればメールは届かず、ネームサーバーが全部止まれば Web もメールも名前を引けない。それならまとめて見ればよい、と考えて作り始めた。DNS のレコードを読むだけなら、DMARC チェッカーと同じくブラウザの中で済むはずだった。
10 の項目は、それぞれ何を決めているか
項目は二つの群に分けている。一つめは、メールにかかわる 6 項目である。
- SPF: そのドメインを名乗ってメールを送ってよいサーバーの一覧
- DKIM: 送信時にメールに付ける電子署名と、その検証に使う公開鍵
- DMARC: SPF と DKIM の確認に失敗したメールを、受信側にどう扱ってほしいかの宣言
- MTA-STS: このドメイン宛てのメールを、暗号化した通信(TLS)で届けるよう送信側に求める仕組み(RFC 8461)
- TLS-RPT: 暗号化した配送に成功したか失敗したかを、送信側から日ごとに報告してもらう仕組み(RFC 8460)
- BIMI: 対応したメールソフトの受信一覧に、送信者のロゴを表示させる仕組み
二つめは、DNS の基盤にかかわる 4 項目である。
- MX: このドメイン宛てのメールを受け取るサーバー
- NS: このドメインの名前を答えるネームサーバーの一覧
- DNSSEC: DNS の応答に電子署名を付け、途中で書き換えられていないことを確かめられるようにする仕組み(RFC 9364)
- CAA: このドメインの SSL/TLS 証明書を発行してよい認証局の指定(RFC 8659)
SPF、DKIM、DMARC の判定には、DMARC チェッカーと同じ部品を使っている。判定のコードを二つのツールの共通部分に置き、ドメイン健康診断の側では、その結果を 5 段階に写して修正例を作るだけにした。DNS の引き方も同じで、ブラウザから Google と Cloudflare の公開 DNS に DNS over HTTPS で問い合わせる(仕組みはブラウザから DNS を引くに書いた)。項目が増えたぶん問い合わせも増えるので、1 回の診断で送る問い合わせの上限は、DMARC チェッカーの 80 回に対して 150 回にしてある。
「未設定」と「確認できず」は、どう違うのか
5 段階のうち、「未設定」はレコードが無いことを確かめられた状態、「確認できず」は有るか無いかを確かめられなかった状態を指す。どちらも「良好」ではないが、利用者がとるべき行動は違う。前者ならレコードを足せばよく、後者なら足す前に調べ直す必要がある。
区別が要る場面の一つは、DNS の応答が SERVFAIL だったときである。SERVFAIL は DNS サーバーが「答えを用意できなかった」と返すエラーで、「そんな名前は無い」という意味の NXDOMAIN とは違う。ところが、DMARC チェッカーと共有している SPF と DMARC の判定部品は、この二つを区別せず、どちらも「レコードなし」として扱う。そのまま使うと、DNS の障害で答えが返らなかったドメインにも「SPF レコードがありません」と表示され、無用なレコードの追加を勧めてしまう。そこでドメイン健康診断では、同じ名前の問い合わせ結果を見直し、SERVFAIL だった項目を「確認できず」に置き換えている。
DNS が SERVFAIL を返す原因はいくつかあり、その一つが DNSSEC の検証の失敗である。
DNSSEC の署名は、自分では検証しない
DNSSEC を検証するには、ルートゾーンから順に鍵と署名を辿り、暗号の計算をしなければならない。ドメイン健康診断はこれを自分ではやらず、公開 DNS が検証した結果を読んでいる。
署名を検証する DNS サーバー(リゾルバ)は、検証に成功した応答に AD(Authenticated
Data)フラグを付ける。Google と Cloudflare が JSON で返す応答にも、このフラグが
AD
として入っている。ツールは、ゾーンの DNSKEY レコード(署名の公開鍵)と、親ゾーンに登録された
DS レコード(その鍵を親が保証するための値)を引き、次のように判定する。
- DS があり、DNSKEY の応答に AD が付いていれば「良好」
- DNSKEY はあるが DS が無ければ、署名はしていても検証につながらないので「注意」
- どちらも無ければ「未設定」
判定に迷うのは、検証に失敗しているドメインである。検証するリゾルバは、署名が合わない応答を利用者に渡さず、SERVFAIL
を返す。これだけでは、ただの障害と見分けがつかない。そこでツールは、同じ問い合わせに
cd=1(検証をしなくてよい、という指定)を付けてもう一度送る。検証を止めると答えが返ってくるなら、名前は存在していて、署名の側が壊れていることになる。この場合は「問題」とし、レジストラに登録した
DS
が古いまま残っていないかを確かめるよう案内する。検証するリゾルバを使っている利用者からは、このドメインの名前が引けなくなっているからである。
自分で検証しない以上、この判定は Google と Cloudflare の検証結果に依存する。項目の説明にも、署名そのものは検証していないと書いてある。
証明書を発行してよい認証局(CAA)
CAA は、証明書を発行してよい認証局をドメインの持ち主が DNS で宣言するレコードである。認証局は発行の前に CAA を確認し、自社が含まれていなければ発行しない。入力した名前に CAA が無ければ、左のラベルを一つずつ外しながら上位の名前を探し、最初に見つかったものを使う。www.example.jp に無ければ example.jp の CAA が適用される、という具合である。ツールもこの順で探す。
書き誤ると影響が大きいのは、フラグの 128(critical)である。RFC 8659 は、このフラグが付いたタグを認証局が理解できなければ、発行してはならないと定めている。綴りを間違えたタグに 128 が付いていると、どの認証局からも証明書が出なくなる。ツールはこの状態を「問題」と判定する。フラグの無い未知のタグは認証局に無視されるだけなので、「注意」にとどめている。
MX と NS は、DNS で引けるところまでを見る
MX では、レコードの有無、MX のホスト名が IP アドレスまで引けるか、CNAME
(別名)を指していないか、IP アドレスを直接書いていないかを確かめる。0 . という
MX は、「このドメインはメールを受け取らない」という宣言(Null MX、RFC 7505)なので、それだけなら「良好」とする。MX が無いのに Web サーバーのアドレス(A
レコード)があるドメインは、送信側がその Web
サーバーにメールを届けようとするので、「注意」として Null MX の例を示す。
メールサーバーに実際に接続して、メールを受け付けるかまでは確かめていない。ブラウザの JavaScript はメールの通信(SMTP)の接続を開けず、このツールはそのためのサーバーも置いていない。画面にも、SMTP の接続はしていないと書いてある。
NS では、ネームサーバーが 2 台以上あるか、それぞれの IP アドレスが引けるか、IPv4 アドレスがすべて同じ /24 のネットワークに収まっていないかを見る。同じネットワークに並んでいると、そのネットワークの障害で全台が止まりうるからである(RFC 2182)。
MTA-STS のポリシーファイルは、なぜブラウザから読めないのか
10 項目のうち 9 項目は、ここまでの方法で DNS から判定できた。残る一つが MTA-STS である。
MTA-STS の設定は二つに分かれている。DNS の _mta-sts.ドメイン に置く TXT
レコードと、https://mta-sts.ドメイン/.well-known/mta-sts.txt
に置くポリシーファイルである。暗号化を強制するか(mode)、どの MX
を正規の受信サーバーとみなすか(mx)といった中身は、ポリシーファイルの側に書かれている。ポリシーの
mx がいまの MX と食い違っていると、mode: enforce
のドメインでは、MTA-STS に対応した送信側がその MX
へ配送しなくなる。判定するには、ファイルを読むしかない。
ファイルは HTTPS で置かれた普通のテキストなので、fetch で取れそうに見える。しかし、ブラウザが別のサイトの応答を JavaScript に渡すのは、相手が CORS のヘッダーで読み取りを許したときだけである。ポリシーファイルを読むのはメールの送信サーバーで、ブラウザから読まれる前提のファイルではないから、このヘッダーが付いているとは期待できない。
そこでこの項目だけ、Cloudflare Workers で動く API を経由することにした。DNS に MTA-STS の
TXT
レコードがあったときに限り、ブラウザがドメイン名をこのツールのサーバーに送る。サーバーはポリシーファイルに
GET 要求を 1
回送り、中身をブラウザに返す。取得したファイルは保存しない。取得先のサーバーから見ると、要求は
Cloudflare のネットワークから otasuke-domain-check/1.0 という User-Agent
で届き、利用者の IP アドレスは伝わらない。
サーバーに取りに行かせるときの制限
利用者の入力を受けてサーバーが外に要求を送る機能は、内部のネットワークを覗く踏み台に使われうる。この危うさ(SSRF)と対策はセキュリティヘッダーチェッカーの記事に書いた。ドメイン健康診断の API は、それより受け取るものを狭くしている。
-
受け取るのは URL ではなくドメイン名だけで、取得先は
https://mta-sts.ドメイン/.well-known/mta-sts.txtに固定している。スキーム、ポート、パスを利用者が指定する余地はない。 -
IP アドレスの直書きや、
localhost、.internal、.localなど内部用の名前で終わるドメインは拒否する。 - Workers の fetch を公開インターネットへの要求として扱わせる設定(global_fetch_strictly_public)を有効にしている。
- リダイレクトは追わない。RFC 8461 の 3.3 節も、ポリシーの取得で HTTP のリダイレクトを追ってはならないと定めている。
- 本文は 64 KiB、全体は 10 秒で打ち切る。64 KB は RFC 8461 がポリシーの大きさの目安として挙げている値である。
- 同じ IP アドレスからの取得は、60 秒に 20 回までにしている(データセンターごとの目安)。
サーバーを通す以上、ドメイン名はこのツールのサーバーに届く。ツールのコードは保存しないが、Cloudflare のアクセスログやエラー調査用のログ(最大 7 日)には、API への要求の一部としてドメイン名が一時的に記録されうる。プライバシーポリシーにもそう書いてある。
届くもの、確かめていないもの
MTA-STS 以外の 9 項目では、ドメイン名はこのツールのサーバーに届かず、Google と Cloudflare
の公開 DNS に届く。診断を終えるとページの URL に ?domain=
が付く。その URL
を開き直せば、ページの要求の一部としてドメイン名がこのツールのサーバーにも届き、閲覧したページの
URL として Google アナリティクスの計測にも含まれうる。この点は DMARC
チェッカーと同じである。
DNS を外から引くだけの診断なので、確かめていないことも残る。
- メールサーバーとの実際の通信で、TLS による暗号化が使われているか
- BIMI のロゴの画像と証明書の中身(URL の形式だけを見ている)
- DNSSEC の署名そのもの(公開 DNS の検証結果を読んでいる)
- よく使われる名前に当たらなかった DKIM のセレクタ(「確認できず」になる)
まとめて見ればよい、という最初の見込みは、10 項目のうち 9
項目までは当たっていた。残りの一つのために、ドメイン名を受け取るサーバーを置くことになった。公開
DNS
にどの名前を何回問い合わせたかは、結果の末尾の問い合わせログに出る。ドメイン名を載せてこのツールのサーバーに送る要求が
/api/mta-sts
だけであることは、ブラウザの開発者ツールでネットワークの一覧を見れば確かめられる。