パスキーの6桁PINが弱くないのは、6桁が強いからではありません。総当たりの試行回数が、ファームウェアとTPMによって物理的に打ち止めになるからです。FIDO2セキュリティキーなら合計8回、Windows HelloならTPMの32回。9回目は存在しません。
「6桁の数字だけ? 英数記号混在で8桁以上だろう」という反論は、パスワードの世界では長く常識でした。ただしその常識は「攻撃者が無制限に試せる」という前提の上に立っています。PINはその前提が成立しない場所にいます。
pinRetries というカウンタが、認証器のどこで数えられ、何回目で何のエラーを返し、そこからどうやって復帰するのか。PINが認証器の外に出ないまま検証される手順も含めて、CTAP仕様とTPMの実装値まで下りて追います。手を動かす話ではありません。読む話です。
ちなみに私は、この説明ができずに一度引き下がっています。仕様書を開けば15分で分かる話でした。開いていなかったのが敗因です。
この記事の位置づけ
1Passwordは、あなたのマスターパスワードを一度も受信していません は、SRPプロトコルでパスワードを送らない話でした。
あなたのパスキーは、フィッシングサイトでは押しても反応しません は、origin binding でそもそも署名が生成されない話でした。
この記事は3本目です。PINを送らない設計と、送らないことで何が可能になったかを扱います。前2本を読んでいなくても単体で完結します。
なお、同じテーマを情シスが会議で説明するための材料として書き直したものを自分のブログに置いています。エラーコードや仕様の話は出てきません。代わりに「なぜ桁数の比較が的外れなのか」を図6枚で説明しています。上司や非エンジニアに読ませる用途ならそちらの方が通ります。
エントロピー計算の落とし穴
まず数字を並べます。よくある比較はこうです。
6桁数字 10^6 = 1,000,000 通り (約 2^20)
8桁英数混在 62^8 = 218,340,105,584,896 通り (約 2^48)
2億倍。この数字だけを見れば「6桁は論外」という結論は自然に出てきますし、実際そう結論した人が会議室にいたわけです。私も最初はそう思っていました。
で、実際に何秒で終わるのか
通り数だけでは体感しづらいので、GPU 1枚で全部試すのに何秒かかるかを出します。RTX 4090 の hashcat ベンチマーク(MD5 164.1 GH/s、SHA-256 63.0 GH/s、bcrypt 約14 kH/s)で計算するとこうなります。
| 保存方式 | 6桁数字を全件 | 8桁英数を全件 |
|---|---|---|
| MD5(ソルト無し) | 6マイクロ秒 | 22分 |
| SHA-256 | 16マイクロ秒 | 58分 |
| bcrypt | 1.2分 | 494年 |
見てほしいのは右の列です。
「英数記号混在で8桁以上」は無制限総当たりに耐えるための数字のはずでした。ところがMD5で保存されていれば、GPU1枚で22分です。昼休みに終わります。長さが守ってくれていると思っていました。実際に守っていたのは bcrypt です。
そして左の列。6桁数字は、もしDBにハッシュで保存されていたら、bcryptですら1.2分で全滅します。 MD5なら6マイクロ秒、まばたきより短い。
ここが本題です。
6桁PINが安全なのは、エントロピーが足りているからではありません。DBに存在しないから、この表のどの行にも当てはまらないのです。
エントロピーの議論は「攻撃者がハッシュを手に入れている」という前提の上で成立します。PINはその前提が作れません。土俵が違う、というのはそういう意味です。
ついでに書いておくと、文字種の混在要求は現在のNISTでは禁止されています。SP 800-63B は SHALL NOT、最も強い禁止形です。
Verifiers and CSPs SHALL NOT impose other composition rules (e.g., requiring mixtures of different character types) for passwords.
定期変更の強制も同じく SHALL NOT。記号必須にすると Password1! が量産され、90日変更を課すと Password2! になるからです。文字種を増やしたつもりで探索範囲が狭まる。
Ubuntu の /etc/shadow が yescrypt(メモリ困難関数)で保存されているのも同じ理由です。長さで殴る方向をやめ、1回の試行を重くして総当たりの経済性を壊す方向に、パスワード側の設計も動いてきました。
PINは認証器の境界を越えない
CTAP 2.1 の PIN/UV 認証プロトコルでは、PINそのものがリライングパーティ(サーバー)に送られることはありません。
流れはこうです。
- プラットフォーム(ブラウザ/OS)が認証器と ECDH で
sharedSecretを確立する - プラットフォームはPINを
sharedSecretで暗号化し、認証器に渡す - 認証器の中でPINを復号し、検証する
- 検証が通ると、認証器は
pinUvAuthTokenを発行して返す - プラットフォームは
pinUvAuthTokenでclientDataHashに対するpinUvAuthParamを計算する - サーバーに届くのは署名と
pinUvAuthParamだけ
PINが登場するのは 2〜3 だけで、そこはUSB/NFC/BLE越しの認証器とプラットフォームの間で閉じています。ネットワークには出ません。
6桁を打ち込むだけの体験の裏で、ECDH鍵交換が走っています。ユーザーが何も知らなくていいのが正しい設計なので、これは仕様どおりです。読んでしまった我々だけが、少し損した気分になります。
Windows Hello も同じ構造です。Microsoft のドキュメントは「PINは端末にローカルで、どこにも送信されず、サーバーには保存されない。サーバーはPINのコピーを持っていない」と、これ以上ないくらいはっきり書いています。持っていないものは漏らせません。
サーバーが保持しているのは公開鍵だけです。 DBが漏れても、そこから総当たりできる対象がありません。
3回と8回の二段構え
回数制限は2層になっています。ここが実装として面白い部分です。
まず全体像です。
| 何回で | 何が起きる | 戻せるか | |
|---|---|---|---|
| 第1層 | 連続3回 | 一時停止 | キーを抜き差しすれば戻る |
| 第2層 | 通算8回 | 完全停止 | リセット以外に戻せない |
第1層: 連続3回で一時停止
PINを3回続けて間違えると、認証器は CTAP2_ERR_PIN_AUTH_BLOCKED を返して受付をやめます。復帰するにはキーを一度抜いて挿し直す必要があります。
なぜ「抜き差し」なのか。仕様に理由が書かれています。パソコンに入り込んだマルウェアが、持ち主の知らないうちにキーをロックし続けられないようにするためです。
わざと8回間違えてキーを使用不能にする。この嫌がらせが成立すると困ります。そこで途中に「人間が手を動かさないと先へ進めない」壁を置きました。抜き差しは、その場に人がいる証拠です。
つまりこの3回は、攻撃者を止めるためだけの数字ではありません。正規ユーザーのキーを守るための数字でもあります。
第2層: 通算8回で完全停止
こちらは戻りません。認証器の中に残り回数のカウンタ(pinRetries)があり、これが0になると CTAP2_ERR_PIN_BLOCKED になります。抜き差ししても同じです。
YubiKey の場合、このカウンタの初期値は8です。正しいPINを1回入れれば8に戻るので、日常的に打ち間違える人が困ることはありません。
問題は0になったときです。復帰手段はFIDO2アプリケーションのリセットしかありません。 そしてリセットすると、PINだけでなくそのキーに登録してあったパスキーも指紋も全部消えます。
攻撃者の側から見ると、これは総当たりが続けられなくなるという話ではありません。9回目に手を伸ばした時点で、盗もうとしていた中身そのものが消えています。
残り回数は次のコマンドで確認できます。
ykman fido info
最小PIN長は4文字
ついでに書いておくと、CTAP仕様の最小PIN長は4バイトです。6桁は仕様上の下限ですらありません。そしてPINは数字に限定されていません。英数字も使えます。
「6桁数字」は多くのサービスがUIの既定値としてそう出しているだけで、仕様が数字6桁を強制しているわけではないという点は、反論の材料として使えます。
プラットフォーム認証器側: TPMのanti-hammering
セキュリティキーを挿さないパスキー(Windows Hello、Touch ID)では、回数を数えるのはTPMやSecure Enclaveです。
Windows + TPM 2.0 の実装値はこうです。
- 認可失敗32回でロック
- 10分ごとに1回ずつ失敗を忘れる
- ロック後の待ち時間は段階的に伸びる: 最初の再起動後1分 → 4回目の再起動後2分 → 5回目の再起動後10分
「10分に1回回復」が効いています。仮に32回を使い切っても、待てば再開はできます。ただし1回の試行あたり10分です。
100万通りを全部試すと 1,000,000 × 10分 = 約19年。当たりが平均して真ん中にあると考えても約9.5年かかります。桁で圧倒する数字ではありませんが、6桁の数字1つを盗むために10年近く1台のPCを占有し続ける攻撃者はいません。
これはソフトウェアのレート制限とは違います。TPMというハードウェアが、鍵の使用そのものを拒否するからです。OSを入れ替えても、ディスクを抜いて別マシンに挿しても、カウンタは付いてきます。逃げ場がない。
PINは認証の主役ではない
ここまでの話を束ねると、PINの位置づけが変わります。主役ではありません。
パスキーの認証本体は公開鍵暗号のチャレンジレスポンスです。サーバーがランダムな challenge を送り、認証器の中に閉じ込められた秘密鍵がそれに署名を返し、サーバーは登録時に受け取っておいた公開鍵でその署名を検証する。この一往復が認証の実体で、秘密鍵は最初から最後まで認証器の外に出ません。出せない、と言った方が正確です。
PINはこの署名を許可するためのローカルなスイッチです。仕様上は User Verification (UV) と呼ばれ、生体認証と同じ枠にいます。指紋の代わりに6桁を打っている。それだけです。役割は「今この認証器を触っているのが持ち主か」の確認であって、認証そのものを担っているわけではありません。
| パスワード | パスキー | |
|---|---|---|
| 要素数 | 1 (知っているもの) | 2 (持っているもの + 知っているもの/本人) |
| ネットワークに出るか | 出る | どちらも出ない |
| サーバーの保持物 | ハッシュ | 公開鍵のみ |
| オフライン総当たりの起点 | DBダンプ | 存在しない |
| 試行回数の制限 | サービス側の実装依存 | ファームウェア/TPM |
| 漏洩時の影響範囲 | 使い回し先まで連鎖 | その端末1台のみ |
PINとパスワードを桁数で比べるのは、家の鍵のギザギザの数と金庫のダイヤルの桁数を比べるようなものです。どちらも数が多いほど強くはなります。ただし「ギザギザ6個とダイヤル8桁のどちらが強いか」という問いに答えはありません。守っている対象も、攻撃してくる経路も、そもそも違うからです。
PINが実際に弱い場面
PINへの懸念が全部的外れかというと、そうでもありません。弱い場面はあります。
- 肩越しの覗き見: 6桁は覚えやすく、見られたら再現されます。長いパスワードより現実的な脅威です
- 端末ごと奪われる: PINを知られた状態で端末を持たれると、8回のうち1回目で開きます。回数制限は「知らない攻撃者」にしか効きません
-
推測されやすいPIN: 生年月日や
123456は8回の枠に十分収まります。桁を増やしても、選び方が同じなら結果は変わりません - 復旧フロー: PIN紛失時の復旧がSMSやメールに依存していれば、そこが攻撃面になります
つまりPINに対する正しい懸念は「桁数が少ない」ではなく、「観察されやすい」と「端末とセットで奪われる」の2つです。反論するときはこの2つを認めた上で話した方が、議論として通ります。
「パスワード側も5回で止めれば同じでは」への短い回答
実装の話をすると必ず出る反論なので、ここだけ触れておきます。
rate limiting 自体は正しい対策で、NIST SP 800-63B も SHALL で要求しています(1アカウントあたり連続100回まで)。ただし担保にはなりません。カウンタの置き場所が違うからです。
- PIN: カウンタは認証器のファームウェア/TPMの中。秘密と同居しているので、試せる場所そのものを握っている
- パスワード: カウンタはアプリケーション層。秘密はその後ろのDBにある。DBが漏れたら、カウンタを通らずに済む
NIST 自身がこう書いています。
Offline attacks are possible when the attacker obtains one or more hashed passwords through a database breach.
だから同じ文書が rate limiting とは別枠でソルト付き反復ハッシュを義務づけています。加えて、クレデンシャルスタッフィング(1回目で通る)とフィッシング(本人が渡す)には回数制限が原理的に効きません。
この論点を図とセットで詳しく書いたものが ブログ側の記事 にあります。
まとめ
- 6桁PINが弱くない理由は、6桁の強さにありません。試行回数がファームウェアとTPMで物理的に止まることにあります
- CTAPは二段構え。連続3回で
CTAP2_ERR_PIN_AUTH_BLOCKED(要電源再投入)、pinRetries枯渇でCTAP2_ERR_PIN_BLOCKED(リセット必須) - 3回制限は攻撃者対策であると同時に、マルウェアによるDoSを防ぐ設計
- YubiKeyの
pinRetries初期値は8。リセットするとパスキーも指紋も全部消えるので、9回目には標的が消滅している - Windows Hello は TPM 2.0 が32回でロック、10分に1回だけ回復。1試行10分なので、100万通りの全件に約19年かかる
- PINはサーバーに送られない。サーバーの保持物は公開鍵だけなので、オフライン総当たりの起点が存在しない
- CTAPの最小PIN長は4バイトで、数字限定でもない。「6桁数字」はUIの既定値であって仕様の要求ではない
- 「パスワードも5回で止めれば同じ」は担保にならない。カウンタがアプリ層にあり、秘密はその後ろのDBにあるため、DB漏洩で迂回される
- 正しい懸念は「覗き見」と「端末ごと奪われる」の2つ。桁数はそこに入っていない
エントロピーの比較は、分子だけを見て分母を見ない議論になりがちです。2^20 と 2^48 を並べる前に、その2^48が何回試せる前提の数字なのかを確認する。パスワードの常識が正しいまま、適用範囲だけがずれている典型例だと思います。
なお冒頭の反論、正面から論破しても提案は通りません。相手の常識を否定した形になるからです。「その8桁の根拠、DBが漏れる前提ですよね」と一度肯定してから入ると、だいたい話が進みます。仕様書より先に、この一言を覚えておけばよかった。面白くいきましょう。
認証情報を外部プロセスやAIエージェントに渡す設計は、パスキーとは別の攻撃面を生みます。「秘密が漏れない設計」から「秘密が使えない環境で漏らさない設計」までを拙著にまとめています。
MCPセキュリティ実践
Sources:
- NIST SP 800-63B-4 Digital Identity Guidelines — Strength of Passwords
- Client to Authenticator Protocol (CTAP) 2.1 — FIDO Alliance
- Understanding YubiKey PINs — Yubico Support
- Resetting the FIDO2 Application on Your YubiKey — Yubico Support
- Windows Hello for Business FAQ — Microsoft Learn
- TPM fundamentals — Microsoft Learn

