会員登録やログインのたびに届く確認メールを開き、コードを急いで探して入力する作業に煩わしさを感じたことはありませんか?
本記事は、Webサービスのこうした煩わしさから離脱率を下げたい開発者や、最新のブラウザ認証基盤に興味がある方を対象としています。
従来の面倒なメール確認を根本から変える新技術「EVP」の全容と、その検証サーバーをJava・Spring Bootで構築した内容をまとめました。
メール確認の手間をゼロにする「EVP」とは
Webサイトの会員登録時、メールに届いた「123456」のような6桁のワンタイムパスワードを、有効期限に焦りながら急いでコピー&ペーストした経験は誰にでもあるはずです。
あの待ち時間を解消する新しい仕組みとして、Chromeの「Email Verification Protocol(EVP)」が提案されています。
従来の確認フローには、画面を行き来する手間に伴う離脱や、入力コードを狙ったフィッシング詐欺のリスクがあります。
EVPでは、ブラウザがGmailなどのメール発行者と裏側で連携します。具体的にはChromeなどでログインしているアカウントがある場合、そのメールアドレスに対して認証の許可を与えるとブラウザがメールアドレスの認証に必要な情報を取得し、サーバーで認証すること所有者であることを担保する技術となります。
これにより暗号署名付きトークンをフォームへ自動で差し込むため、ユーザーは確認コードを意識する必要がありません。
現在はChrome 150以降のOrigin Trialとして提供されており、Gmailが先行対応しています。
具体的には以下のURLでChromeが公開している説明ページになります。
サンプルコード
メールアドレスを入れて許可をすると以下のように認証された結果が表示されます。
許可をしていないメールアドレスは、通常の認証のルートへ入るようになります。
EVPトークンを支える「2層構造」の仕組み
EVPでやり取りされるトークンは、単純なJWTではありません。
「RFC 9901(SD-JWT)」に基づいた2層構造を採用しています。
中央のチルダ記号(~)を挟み、役割の異なる2つのトークンが連結された形です。
<発行者が署名した EVT> ~ <ブラウザが署名した KB-JWT>
前半の「EVT」は、Gmailなどの発行者が「このメールは本人のものです」と署名したトークンです。
ここにはブラウザが生成した使い捨ての公開鍵(cnf.jwk)が埋め込まれています。
後半の「KB-JWT」は、ブラウザが「今この瞬間に、このサイトに向けて私が送信しました」と署名したトークンです。
発行者が署名した一時鍵を用いてブラウザ自身が署名するため、第三者による持ち出しや使い回しを防ぐことができます。
サーバー側で実行する5段階の検証ステップ詳細
今回、この仕組みをJava 21とSpring Bootを使って検証サーバーとして実装してみました。
サーバー側がトークンを受け取ったときは、以下の5つのステップを順番に検証していきます。
途中で1つでも条件を満たさない場合は、その場で検証を中断して即座に失敗を返す設計です。
具体的には細かく説明しますが、全体像としては以下のようになります。
Step 1: SD-JWT 解析
受け取ったトークン文字列を ~ 記号で分割し、前半のEVTと後半のKB-JWTに切り離します。
Nimbus JOSE+JWT ライブラリを用い、それぞれを SignedJWT オブジェクトとしてパースして解析可能な状態にします。
Step 2: クレーム検証
トークン内部に含まれる各データ(クレーム)の正当性を確認します。
email_verified の確認: true であるかをチェックします。
メールアドレスの照合: フォームから送られた入力値と、EVT内のアドレスを大文字小文字を無視して比較します。
オリジン(aud)の確認: KB-JWTの宛先が自サーバーのオリジン(例: http://localhost:8080 )と一致するか確かめます。
nonce の照合: サーバー側でセッションごとにあらかじめ発行したランダム値と、KB-JWT内の値が完全一致するか確認し、リプレイ攻撃を防ぎます。
有効期限(exp)の確認: サーバー間の時刻のズレを吸収するため、60秒の猶予(leeway)を持たせて期限切れでないかを検証します。
Step 3: DNS 委任検証
EVTに書かれた発行者URLが、本当にそのメールドメインの正当な委任先であるかを確認します。
メールアドレスのドメインから _email-verification. というDNS TXTレコードをJNDI経由で問い合わせます。
TXTレコード内に記載された iss=<発行者ホスト名> と、EVT内の発行者ホスト名が一致するかを比較することで、自称発行者によるなりすましを防ぎます。
Step 4: 発行者メタデータと公開鍵(JWKS)の取得
発行者のURL末尾に /.well-known/email-verification を付与してHTTP GETを行い、メタデータを取得します。
メタデータ内に定義された jwks_uri から、署名検証に必要な最新の公開鍵セット(JWKS)を自動でフェッチしてメモリにキャッシュします。
Step 5: 暗号署名の厳密な検証
最後に、3つの要素に対する暗号署名とハッシュを検証します。
EVTの発行者署名: 取得したJWKSの鍵を用いて、EVTの署名を検証します。
KB-JWTの署名(Key Binding): EVT内の cnf.jwk に含まれるブラウザの一時公開鍵を取り出し、KB-JWTの署名を検証します。
ハッシュ値(sd_hash)の一致: EVT全体のSHA-256ハッシュ値を算出し、KB-JWT内の sd_hash と一致するか確かめることで、トークンの改ざんや貼り合わせを排除します。
なぜ4つのパラメータが必要なのか
EVPの安全性を支えているのは、nonce、aud、sd_hash、そしてエフェメラル鍵の4点です。
過去のトークンをそのまま再送信する攻撃を防ぐために、セッションごとに発行されるランダムなnonce値を用います。
また、正規のトークンを別の悪意あるサイトへ持ち出せないよう、提出先のオリジン(aud)を検証します。
さらに、EVTの改ざんを防ぐハッシュ値(sd_hash)と、ブラウザの一時鍵による署名が組み合わさっています。
これらが揃うことで、「今、このブラウザが、このサイトのために提示したトークンである」ことが暗号学的に保証されます。
まとめ
今回の機能によってこれまでメールに数桁の数値に送信したり、URLでの認証である程度担保されていた認証が厳密にチェックできるようになります。特に昨今のAIによる攻撃を考えると、これまで人だからある程度防ぐことができていた認証を突破されることが多くなっていることを考えると、ブラウザの認証と組みわせることで本人なのかの認証ができるようになるのは有用かと思いました。
現状はGmailしか対応していない部分が、今後どこまでのメールプロバイダーが対応していくかが広がるかどうかの分かれ道になるかなと思いました。



