転職の前、新しいサービスの開設前、古いパスワードの末尾に「!」を足す前。多くの人は先に検索する。このパスワードは流出していないか。結果には「パスワード強度チェック」「Have I Been Pwned」「オンライン漏洩確認」が並ぶ。見出しは同じ問いに答えているように見える。開くと手順は分かれる。入力欄の中身を自社サーバーへ POST するページ、第三者へハッシュの末尾一覧を取りに行くページ、公開された弱いパスワードの単語表だけを落としてタブ内で照合するページ。三つの流れは、「パスワードがこのページを出たか」への答えがまったく違う。

本稿は、弱・中・強のメーターの説明ではない。他人のデータベースを攻撃する手順でもない。問いは一つだけだ。公開名簿を端末で照合することと、パスワードを大規模な漏洩照会へ渡すことは、それぞれ何を証明できて、何を証明できないか。MyPassGen の強度チェックは前者に属する。検査中のパスワードは送らない。照合するのは、ページと一緒に読む公開された弱いパスワード名簿だ。ツールは登録なしで開く。境界を先に広げ、開発者ツールでその場で確かめる。

東京都のサイバーセキュリティ啓発や、内閣サイバーセキュリティセンターのハンドブックは、メールアドレスやパスワードの流出確認として Have I Been Pwned を挙げることが多い。それは「信頼できる照会先の一例」であり、「検査中の文字列が端末を出ない」と同義ではない。日本語の検索結果でも、メールアドレス検索とパスワード検索と強度メーターが、一文に潰されやすい。先にプロトコルを分ける。

「パスワードを調べる」は三種類ある

先に名前を揃える。「ローカル」「送信しない」「漏洩庫と照合した」は、同じ宣伝文に同居できる。指している通信は三つだ。

やり方 ブラウザを出るもの 答えられる問い
パスワードの平文をサイトへ渡す 平文、または元に戻せるフォーム欄 相手のサーバーが「調べた」と言う。根拠は見えない
k-匿名性の範囲照会(HIBP など) SHA-1 または NTLM ハッシュの先頭 5 桁(十六進) そのハッシュが、相手の維持する大規模な漏洩データベースに載っているか
公開された弱いパスワード名簿を端末で照合 静的な単語表。入力欄の中身ではない 123456 や password のような、繰り返し使われた弱いパスワードか

一番目は一番楽で、一番確かめにくい。「照会後すぐに削除します」と書けても、ログ、バックアップ、解析スクリプトは写しを残せる。二番目は Have I Been Pwned の Pwned Passwords が公開している手順だ。ブラウザが先にハッシュを計算し、先頭だけを api.pwnedpasswords.com/range/{prefix} へ送り、末尾の一覧を手元で照合する。三番目は先頭すら送らない。単語表は公開されたよくある弱いパスワードで、件数は数百から一万程度だ。「インターネット上のすべての流出」ではない。

米国標準技術研究所は NIST SP 800-63B-4 で、検証側がパスワードの設定や変更時に「よく使われる、予想しやすい、すでに漏洩した」値の名簿と照合すること、そして大文字・数字・記号の強制ルールを重ねないことを求めている。名簿の主目的は、オンライン推測で先に試される一群を止めることだ。名簿がレート制限の窓より大きくなっても、追加行の効果は小さい、とも書いている。端末の上位名簿の位置は、そこだ。よくある弱いパスワードの最初のふるいであり、インターネット上の全件検索ではない。

先に問いを分けてから、ツールを選ぶ

「辞書攻撃で先に当たるか」と「特定の流出案件に載っているか」は、別の一文だ。端末の名簿は前者に答える。大規模な範囲照会は、後者のうち「相手のデータベースが拾っている範囲」に答える。どちらも、「同じパスワードを複数サイトで使い回さない」の代わりにはならない。

平文をサイトへ渡すとは何か

いまも「オンラインパスワード検査」の一部は、入力欄の文字列をリクエスト本文へそのまま入れる。JSON の password 欄、クエリー文字列、一度だけ可逆変換して POST する実装がある。ブラウザから見れば、ログインフォームと本質は同じだ。対向ホスト、リバースプロキシ、アクセスログは、この一回の送信を読める。

HTTPS が守るのは、経路上の盗聴者だ。対向が平文をディスクへ書くこと、解析スクリプトが欄を転送することを禁じない。減らしたいのはこの一回の検査で、知っている人が一人増えたかどうかだ。HTTPS の仕事を書き換えているわけではない。前稿ブラウザで暗号化したとき、平文が送られていないことをその場で確かめると同じ原則だ。スローガンは証拠にならない。通信は証拠になる。

もっと静かな渡し方もある。検査中のパスワードをアドレス欄のクエリーへ書き、同僚へ転送する、チケットへ貼る。クエリーはリクエスト行に乗り、対向のログにも残る。第三者リソースの Referer に、パスと query が残ることもある。その文字列がいま使っている口座のパスワードなら、自分で拡散している。正しい既定値は一つだ。本物のパスワードは、今のタブの入力欄に残す。URL へ書かない。原文を上げる「検査サイト」へ貼らない。

ハッシュ先頭だけを送る大規模照会

Have I Been Pwned の範囲 API は、「大きな庫を探す」と「原文を渡す」を分けている。公式の手順はこうだ。パスワードを UTF-8 で符号化し、SHA-1 を計算する(NTLM も選べる)。ハッシュの先頭 5 桁(十六進)を取り、GET https://api.pwnedpasswords.com/range/{先頭5桁} を送る。応答は末尾と出現回数の組で、コロン区切りだ。クライアントは手元で先頭と末尾を繋ぎ、完全一致があるかを見る。Troy Hunt は Understanding Have I Been Pwned's Use of SHA-1 and k-Anonymity で、このモデルを k-匿名性と呼ぶ。サーバーが見るのはハッシュのバケットであり、完全なハッシュでも、パスワードの原文でもない。

Cloudflare が HIBP と組んだときに公開した数字は、いまも照合できる。先頭を 5 桁にしたとき、同じバケットのハッシュ数の中央値はおよそ 305、応答サイズの中央値はおよそ 12.2 KB。公式は Add-Padding: true も用意する。各応答をおよそ 800–1000 行へ埋め、「応答が短い=よくあるパスワード」という脇道を細くする。範囲照会は、平文を POST するよりずっと抑制されている。それでも外向きの要求は一回ある。ネットワークには api.pwnedpasswords.com へのアクセスが出る。回線を切れば、この種の照会は失敗するはずだ。

範囲照会は「完全な匿名」でもない。先頭は、165 個のバケットのどれに落ちたかを観察者へ教える。ページのスクリプトが、先頭を送る前に原文を別へ保存すれば、k-匿名性は助けない。メールアドレス検索とパスワード検索も、同じ API ではない。HIBP v3 のメールハッシュ範囲照会は先頭 6 桁で、別の機能だ。本稿が比べるのは「このパスワードがパスワード用のデータベースに載っているか」だけだ。メール購読やドメイン監視を、同じ検査へ混ぜない。

だから「HIBP を使っている」を「パスワードは端末を出ていない」と書くのは正確ではない。正確な文はこうだ。原文と完全ハッシュは、設計どおり相手へ渡さない。先頭と、範囲要求の一回は出る。大規模な漏洩データベースを維持する第三者にハッシュのバケットを知られてよいなら、それは妥当な工学上の折衷だ。この検査では先頭すら出したくないなら、次節の端末名簿へ進み、対象範囲が狭いことを受け入れる。

端末の名簿は単語表だけ取り、パスワードは出さない

端末照合の流れは逆だ。先に公開された弱いパスワード一覧を、今のオリジンへ落とす。スクリプトは集合探索をする。ネットワークに単語表が出てよい。入力欄の検査対象が出てはいけない。単語表自体は公開データだ。典型的な出どころは、OWASP / Daniel Miessler 周辺の SecLists にある、「一千万件から切った上位 N」といったファイルだ。答えるのは「攻撃者の辞書が先に試すか」であり、「未公開の流出に載っているか」ではない。

MyPassGen の強度チェックは、この境界で動く。ページを開くと data/leaked-top10k.txt を取る。いまはおよそ 860 行、1 行 1 件の小文字パスワードで、先頭は 123456、password、qwerty のような繰り返し現れる弱い値だ。照合はブラウザ内で終わる。先に小文字へ揃え、限られた変異を作る。@ / 4 を a、0 を o と見るよくある leet、末尾 1〜3 桁の数字を落とす、など。集合に入れば弱と付ける。長く見えても例外はない。単語表の読み込みに失敗したとき、スクリプトは内蔵の最小集合(password、123456 など)へ戻る。外部の漏洩 API へ切り替えない。

強度評価は、もう一本の端末計算だ。文字プールは小文字・大文字・数字・記号の有無で足す(26 + 26 + 10 + 32)。エントロピーは、長さに log2(文字プール) を掛けた近似で、同じ文字が多すぎると割り引く。しきい値はおよそ、40 bits 未満が弱、60 未満が中、80 未満が強、それ以上が極強。8 文字未満は別に「弱い」と出す。ランダム生成は 6–128 文字、既定は 16。オフライン総当たりは毎秒 1010 回、オンライン制限は毎秒 103 回として見積もる。特定の GPU への約束ではなく、自分が見る桁の目安だ。解析イベントは等級と名簿への当たりだけを残す。パスワードの原文は統計へ送らない。

これらの数字は、この端末で照合できる。単語表の行数はエディタか wc -l で数える。ネットワークには単語表が出てよく、入力は出ない。それでも「インターネット全体を調べた」とは書けない。860 件は、SecLists が公開する上位 1 万件すら覆い切らない。HIBP のようなハッシュ索引の大規模データベースとは比べ物にならない。当たらなかったときの、正直な結論は一つだ。この名簿にある、よくある弱いパスワードではない。

名簿に当たらなくても、「安全」とも「未流出」とも書かない

端末の名簿が止めるのは、辞書の先頭の一群だ。名簿の外の流出、リスト型攻撃の組み合わせ、自分のサイトを狙った推測には、同じ文字列が残れる。新しいパスワードが要るときは、この端末で生成する。元の語の後ろに西暦や感嘆符を足して使い続けない。

強度バーは、未流出の証明にならない

強度バーが見積もるのは探索空間であり、収録済みデータの横断検索ではない。20 文字のランダムパスワードはエントロピーが高くても、他人の流出ファイルにすでに載っていれば、攻撃者は文字種の先頭から歩かない。手元の一覧で突く。逆に Password1! は、「大文字・数字・記号が要る」フォームを騙しやすい。公開された弱いパスワード名簿には、ほぼ必ず入る。NIST が組み合わせルールをやめた根拠は、この迂回が多すぎることだ。

検査ページが強度と名簿の両方を出すなら、名簿が強度を上書きする。当たれば弱だ。バーはまだ役に立つ。「短すぎる」「文字種が一種だけ」「qwerty のキーボード並び」を知らせる。それ単体で「使い続けてよい」の証明書は出せない。必要な判断は、たいてい二文だ。名簿に当たった、または 8 文字未満——今すぐ替える。当たらず、十分長い——それでもサイトをまたいで使い回さない。重要な口座は、パスワード管理ツールか端末の生成器で、別の一行を書く。

「大規模照会で当たらなかった」も終点ではない。範囲照会は、相手のデータベースの収録範囲と更新のリズムに依存する。そこに無い未公開流出、内部システムのパスワード、まだハッシュへ入っていない変異は出ない。端末の 860 件よりは広い。それでも他人が維持する標本だ。二つを重ねて使ってよい。前提は、それぞれ何が外へ出て、失敗が何を意味するかを分けられることだ。

その場で確かめる:単語表は出てよく、パスワードは出ない

下の手順は、ブランドの約束に依存しない。捨ててよい検査用パスワードを使う。password、またはどの口座でも使ったことのない仮の列。いま使っている本物のパスワードを、実演へ貼らない。

  1. 開発者ツールのネットワークを開き、「ログを保持 / Preserve log」を入れる。先に Fetch / XHR で絞り、文書とその他の要求も一度見る。
  2. 検査ページを再読み込みする。静的な名簿が見えるはずだ(本サイトでは leaked-top10k.txt)。その要求を開く。応答は 1 行 1 パスワード、リクエスト本文は空であるべきだ。
  3. 入力欄に検査用パスワードを打ち、結果を待つ。増えた要求を見る。URL、query、JSON、フォーム欄のどれにも、今打った列は載らない。
  4. パネルで pwnedpasswords、hibp、range/ を探す。端末名簿の設計なら、これらのホストは出ない。出たら範囲照会を走っている。前節の基準で読む。
  5. 解析要求も見る(経路に matomo や collect が付きやすい)。イベント名に「弱 / 中 / 強」はあってよい。値にパスワードの原文は無い。
  6. 大規模照会側と比べるときは、同じ捨ててよいパスワードで、HIBP を呼ぶページを一度試す。api.pwnedpasswords.com/range/ への GET が見えるはずだ。パスは十六進 5 桁だけで、原文は無い。

回線を切ってもう一度入力するのは、端末方式の加点確認だ。単語表がキャッシュ済みなら、強度と当たりはまだ計算できる。範囲照会は失敗するはずだ。「要求がゼロ」を唯一の合格線にしない。初回は単語表を落とす必要がある。それはパスワードを POST する通信とは別物だ。前稿のブラウザ内暗号化も、同じパネルを使った。ここでは一件だけを見る。業務要求に、自分の入力が載っているか。

よくある勘違い:緑のバー、全件照会、調べたから安全

「強度バーが緑なら流出していない」は成立しない。緑が述べるのは文字空間だ。流出が述べるのは、他人が同じ文字列をすでに持っているかどうかだ。二つは同時に真になれる。

「端末計算と書いてあるから、外向きは無い」も成立しない。端末名簿は単語表を落とす。範囲照会はハッシュのバケットを落とす。禁じたいのは、原文と完全ハッシュが業務データとして出ることだ。HTTP そのものを禁じる必要はない。

「HIBP に無かったから、すべてのサイトで使い回してよい」は、いちばん高い勘違いだ。リスト型攻撃は「一つのパスワードを多くのサイトへ当てる」。そのパスワードが公開上位一千件にいる必要はない。端末名簿も大規模照会も、使い回しは解けない。新しいパスワードはこの端末で生成する。ランダム方式なら既定の 16 文字以上を使い、サイトごとに別の列を書く。

「強いか見て」とチャットへパスワードを貼る行為は、検査を拡散へ変える。新しいパスワードを人へ渡す必要があるときは、短い秘密はワンタイムへ置き、鍵は URL の # の後ろに残す。何度も復号するファイルはファイル暗号へ置く。検査ページが解くのは「自分で名簿と強度を先に見る」ことだ。鍵の交換ではない。

どこから手を付けるか

まず、捨てる予定のパスワード、または本物の口座で使ったことのない列を一回測る。検査ページを開き、ネットワークの単語表要求を見る。入力したあと、その列が外へ出ていないことを確認する。名簿に当たったら、二文字だけ変えて使い続けない。パスワード生成で、この端末で 16 文字以上を作る。当たらなかったら、この公開された弱いパスワード集合の一員ではない、とだけ書く。保存はサイトごとに分ける。

本当の問いが「このパスワードは、既知の大規模流出に載っているか」なら、端末の八百余件では足りない。k-匿名性の範囲照会を走り、ネットワークで「十六進 5 桁だけが出た」と確かめられるツールを使う。来歴の分からない照会ページへ原文を渡さない。両方を終えても、登録なしで開く検査は、各サイト自身のログイン保護の代わりにはならない。サーバーが今後漏らさないことの証明にもならない。

ここまで終われば、見出しの差に答えられる。端末の漏洩名簿が証明するのは、「攻撃者が先に試す一群に似ているか」だ。大規模なパスワード照会——実装が正しければ——が証明するのは、「相手のハッシュデータベースに完全一致があるか」だ。前者は単語表が出て、パスワードは出ない。後者は先頭が出て、原文は出ない。原文を POST する三番目は、どちらにも属さない。

よくある質問

端末で当たらなかったら、Have I Been Pwned も見るべきか。

欲しい一文による。当たらなかったことは、この公開された弱いパスワード名簿の一員ではない、としか言わない。HIBP が収録するパスワードハッシュ庫に載っているかも知りたいなら、範囲照会を別に行い、ネットワークで先頭 5 桁だけが出たことを確認する。二つは分けてよい。「インターネット全体を調べ終わった」へ潰さない。

範囲照会は、自分のパスワードを HIBP へ教えるか。

公式の設計と Troy Hunt の説明では、相手が受け取るのは SHA-1 または NTLM ハッシュの先頭 5 桁であり、パスワードの原文でも、完全ハッシュでもない。照合は自分のブラウザで終わる。それでも外向きの要求は一回ある。先頭すら端末を出したくないなら、端末名簿だけを使い、対象範囲が狭いことを受け入れる。

パスワード検査に登録は要るか。検査中のパスワードは上がるか。

登録は要らない。端末方式では、検査中のパスワードは入力欄に残るはずだ。ブラウザを出てよいのは単語表と、原文を含まない解析イベントだ。実現しているかはネットワークで見る。「送信しません」の文言では見ない。

強度の「オフラインなら N 年」は信じてよいか。

固定の推測速度で付けた桁の目安であり、特定の GPU や単語表攻撃の測定ではない。名簿に当たったとき、攻撃者はその時計どおりには総当たりしない。「短い / 文字種が少ない」の参考にする。安全保証にはしない。