メールの「ソースを表示」を開くと、本文の上に数十行の英数字が並ぶ。メールヘッダと呼ばれる部分で、差出人や件名のほかに、そのメールがどのサーバーを通ってきたか、受信側がなりすましをどう判定したかが書き込まれている。
メールヘッダ解析 は、これを貼り付けると、経由したサーバーの順路と認証の結果を日本語の表にするツールである。ヘッダには宛先のメールアドレスや社内のサーバー名が含まれるので、よそのサーバーに送りたくないものの代表でもある。このツールは、貼り付けた文字列をどこにも送らない。
送らないで済むのは、やることが文字列を読むことだけだからである。DNS も引かないし、署名の検証もしない。それで判定の役に立つかどうかは、ヘッダのどの行を信じるかにかかっている。
ヘッダは下から上へ積み上がる
メールは、送信者のサーバーから受信者のサーバーまで、いくつかのサーバーに順に受け渡されて届く。受け取ったサーバーは、そのたびに
Received:
という行をヘッダの一番上に書き足す(RFC 5322)。だから、ヘッダを上から読むと受信者に近い順、下から読むと送信者に近い順になる。
要点だけ残して抜き出すと、こういう形をしている(架空の例)。
Received: from mail.sender.example.com ([203.0.113.25])
by mx.google.com with ESMTPS ...;
Mon, 14 Sep 2026 21:05:33 -0700 (PDT)
Authentication-Results: mx.google.com;
dkim=pass header.i=@sender.example.com ...;
spf=pass ... smtp.mailfrom=hanako@sender.example.com;
dmarc=pass (p=REJECT ...) header.from=sender.example.com
Received: from [192.168.1.10] ([198.51.100.7])
by mail.sender.example.com (Postfix) with ESMTPSA ...;
Tue, 15 Sep 2026 13:05:31 +0900 (JST)
下の Received: は、送信者のパソコンから送信サーバー mail.sender.example.com
が受け取った記録である。上の行は、その送信サーバーから Gmail の受信サーバー mx.google.com
が受け取った記録になる。ツールはこの行を逆順に並べ直し、1 番目を送信側として順路を表示する。
どこで時間がかかったか
Received:
の末尾には、そのサーバーが受け取った時刻が付いている。隣り合う行の時刻の差を取れば、その区間にかかった時間がわかる。
上の例では、時刻の書き方が揃っていない。下は日本時間(+0900)、上は米国太平洋夏時間(-0700)で書かれている。日本時間に直すと、下が
13 時 05 分 31 秒、上が 13 時 05 分 33 秒で、この区間は 2
秒である。サーバーごとに時間帯の書き方が違うので、ツールはすべて同じ基準の時刻に変換してから差を取る。JST
のように時差を略称だけで書くサーバーもあり、よく出てくる略称は数値の時差に置き換えている。
差がマイナスになることもある。後で受け取ったはずのサーバーの時刻が、前のサーバーより早い。サーバーの時計がずれているときに起きる現象で、数秒のずれは珍しくない。ツールは各区間が全体の何割を占めたかも表示するが、マイナスの区間は割合の計算から外している。この計算方法は、Microsoft が MIT ライセンスで公開しているヘッダ解析ツールの処理を移植したものである。
認証の結果は、受信サーバーが書いている
例の真ん中にある Authentication-Results: が、なりすまし判定の結果である(RFC 8601)。受信サーバーは、メールを受け取った時点で SPF(送信元サーバーの IP
アドレスが、そのドメインに許可されたものか)、DKIM(電子署名が正しいか)、DMARC(その二つの結果が差出人のドメインと一致するか)を確かめ、結果をこの行に書き込む。
SPF や DKIM を自分で確かめるには、送信元ドメインの DNS を引き、署名を計算し直す必要がある。このツールはそれをせず、受信サーバーが書き込んだ判定を読んで表示する。受信サーバーは、メールが届いた瞬間の DNS の状態と、実際に接続してきた相手の IP アドレスをもとに判定している。後から第三者が同じ判定をやり直そうとしても、接続元の IP アドレスはヘッダに書かれた値を信じるしかなく、DNS の設定もその後に変わっているかもしれない。届いた時点の判定を読むほうが、やり直すより確かなことが多い。
表示する前に、書かれた結果を二つの向きから整理している。一つは、差出人欄(From:)のドメインと、SPF
で確かめたドメイン、DKIM
で署名したドメインが一致しているかである。完全に同じか、同じ組織のドメイン(たとえば
mail.example.co.jp と example.co.jp)かを区別して表示する。組織の判定は、co.jp や co.uk
のような主要な区切りを持つ名前だけを一覧で持つ簡易なもので、すべてのドメインを正しく扱えるわけではない。もう一つは、pass
や
fail
といった結果の語を「適合」「要対応」といった日本語の判定と、次にやることの説明に置き換える作業である。
その行は、誰が書いたのか
ヘッダがただの文字列だということは、送信者も好きに書けるということである。悪意のある送信者が、dmarc=pass
と書いた Authentication-Results:
を自分で付けて送ってきたら、どう見分けるのか。
手がかりは、ヘッダが上へ積み上がる性質にある。受信者のサーバーが書き足した行は、送信者が用意したどの行よりも上に来る。送信者が偽の行を仕込んでも、それは受信サーバーの行より下に埋もれる。そのため、このツールは
Authentication-Results:
が複数あるとき、SPF・DKIM・DMARC のいずれかを含むもののうち一番上のものを使う。
ただし、この読み方は、受信サーバーが自分で結果を書き込んでいることを前提にしている。受信サーバーが
Authentication-Results:
を書かない環境で、送信者が偽の行を仕込んでいたら、一番上に来るのはその偽の行である。RFC 8601
は、受信サーバーに対して、自分の名前を騙る行が外から届いたら削除するよう求め、読む側に対しては、行の先頭に書かれたサーバー名(例では
mx.google.com)が自分の組織で使っているものでなければ信用しないよう求めている。このツールはどのサーバー名を信頼するかまでは判定していない。表示されたサーバー名が、自分の使っているメールサービスのものかどうかは、読み手が確かめる必要がある。
受信サーバーが結果を書いていないメールもある。送信済みフォルダに残っている自分のメールがそうで、まだ誰にも受信されていないので判定の行がない。この場合、ツールは結果を「未確認」とし、相手に届いたメールのヘッダを使うよう案内する。
文字列を読むだけのツールに要る部品
やることが文字列の読み取りだけでも、メールヘッダの書式は素直ではない。長い行は途中で改行して次の行を空白で始める(折り返し)決まりがあり、日本語の件名は
=?ISO-2022-JP?B?...?=
のような符号に変換して書かれる。Received:
の中身はサーバーの種類ごとに書き方が違い、括弧の中にさらに括弧が入ることもある。
この部分は、既存のライブラリに任せた。折り返しの解除と日本語の復元は postal-mime、Received:
と Authentication-Results: の分解は mailauth の一部を使っている。mailauth
は署名の検証まで行うライブラリで、全体としてはサーバー側の Node.js
を前提にしており、そのままではブラウザで動かない。文字列を分解するだけの二つのファイルは
Node.js の機能に依存していなかったので、その部分だけを読み込んでいる。
こうして、サーバーに何も送らず、DNS
も引かずに、順路と判定を表にできた。その代わり、表示する判定は受信サーバーが書いた内容を超えられないし、書き手が本物かどうかの最後の確認は読み手に残る。結果を読むときは、一番上の
Authentication-Results:
のサーバー名が、自分の使っているメールサービスのものかを一度見てほしい。