マイナンバーカードで楽天証券にログインしてみた — リーダーレス認証器が実在の金融機関で通った話

前回、マイナンバーカードを USB リーダー無し(スマホ NFC)で WebAuthn 認証器にするところまで作りました。ただしテスト相手は webauthn.io という検証用サイト。本当の問いは「実在のサービス、それも厳しめの金融機関で通用するのか?」です。今回、ちょうどパスキーを必須化しつつある楽天証券(自分の口座)で、登録とログインの両方を試しました。結論から言うと——通りました。専用ハード無し・マイナカードの所持と PIN だけで、本番口座にログインできました。

先に明確にしておきます。これは楽天証券の脆弱性の話では一切ありません。自分の口座に、正規の「パスキーを作成」手順で、自作の認証器を1つ追加登録しただけです。むしろ「楽天のパスキー実装が標準に忠実だから、第三者実装の認証器でも受け入れられた」という、健全さの証明でもあります。既存のログイン手段(パスワード等)は残したまま試しています。

まず「相手の要求」を、口座に手を付けず覗く

本番の金融口座で行き当たりばったりに登録を試すのは怖い。そこで拡張機能に recon(偵察)モードを仕込みました。navigator.credentials.create() / get() が呼ばれたら、RP(楽天)が要求してくるオプションをコンソールに出力して、そこで処理を中断する——カードにもサーバにも一切コミットせず、要求だけを読み取る安全弁です。

分かった楽天の要求はこうでした。

  • residentKey: required — ユーザー名レス(discoverable credential)必須
  • userVerification: required — 生体/PIN による本人検証フラグ必須
  • pubKeyCredParamsES256(-7)あり、EdDSA(-8)は無し
  • attestation: “direct” — アテステーション(認証器の素性証明)を要求

ES256 は我々のサイトごとに導出する P-256 鍵でそのまま対応できる。UV フラグも resident key も実装済み。問題は最後の attestation: "direct" でした。

最大の分岐点 — プライバシーを捨てずに通るか

我々の認証器は、プライバシーのためあえて素性を明かしません。アテステーションは fmt: none、AAGUID(認証器の型番 ID)はすべてゼロ。これは「サイトをまたいで同一カードだと名寄せされない」ための設計です(耐タンパー vs プライバシーの記事参照)。

一方、楽天は direct(素性を見せろ)を要求している。ここが正面衝突します。ただし WebAuthn の仕様上、direct はあくまで「要求」であって、認証器は none を返してよく、それを受理するかどうかはサーバ側のポリシー次第。金融機関なら型番のホワイトリスト(FIDO Metadata Service)で弾く可能性も十分あり——これはクライアントからは判別できず、実際に投げてみるしかない領域でした。

登録 — 「パスキーの作成が完了しました」

recon を外し、スマホのリレーを準備して、楽天の「パスキーを作成」を押す。スマホにカードをかざすと、カードの署名を種に P-256 鍵が導出され、fmt: none のアテステーションが組み上がって楽天へ。返ってきたのは:

パスキーの作成が完了しました。

楽天は fmt: none / AAGUID=ゼロ を受理しました。つまり、プライバシー型の認証器を拒まなかった。事前に心配していた「金融機関は型番検証で弾くのでは」は、少なくとも楽天では杞憂でした。プライバシーを一切捨てずに、本番口座へ登録できたわけです。

ログイン — そして、ここで一度つまずいた

残るはログイン。ところが「パスキーでログイン」を押しても、我々の認証器に処理が回ってこない。調べると、楽天は passkey ログインを条件付き UI(autofill 型、mediation: 'conditional')で呼んでいました。我々の拡張は当初、autofill の自動 get を OS 側(Touch ID)に素通しする作りだったため、マイナが出番を得られていなかったのです。

正直に書くと、ここで私は計測ミスで一度「Myna では入っていない」と誤判定しました。リレーは個々のリクエストをログに残さず、拡張も get 成功時にコンソール出力しない——その2つの「無音」を根拠に早合点したのです。本当の証拠はスマホ側の応答ログでした。条件付き UI の get も拾うよう拡張を直し、再挑戦。今度はスマホにこう出ました。

リクエスト受信: sign / rakuten-sec.co.jp
PIN残:3
✅ 応答送信 (sign/es256, pub 64B)

楽天が呼んだ get() を拡張が横取りし、リレー経由でスマホへ。カードで ES256 署名を生成して返すと、楽天が登録済みのマイナ公開鍵で検証し——ログイン成立。会員ページが開きました。専用リーダーもパスキー対応端末も使わず、マイナカードを当てるだけで、実在の証券口座にログインできた瞬間です。

これが意味すること

webauthn.io のデモとは重みが違います。相手はセキュリティ最重要の金融機関、しかもパスキー必須化を進める実サービス。そこで、

  • 専用ハード不要:全員が持つスマホの NFC でカードを読む
  • 所持 + PIN:マイナカードという強い所持 factor で、パスワードレスにログイン
  • プライバシー維持:サイトごとに別鍵、AAGUID ゼロで名寄せ不可、マイナンバーも証明書も漏れない

——が同時に成立した。「全員が既に持っている国民 ID カードを、フィッシング困難な WebAuthn 認証器にする」という当初の構想が、実在の金融サービスで端から端まで動いたことになります。

正直な限界

  • 耐タンパー性:鍵の導出・署名はソフト側で行うため、専用キー(YubiKey 等)には及びません。位置づけは「金庫」ではなく「全員のカードで所持+PIN のフィッシング耐性を低コストに足す層」。
  • リレーはまだプロト:今回の Wi-Fi リレーは疎通優先で近接性の証明が無い。本命は BLE トランスポート(電波到達=近接=遠隔リレー対策)です。
  • 運用上の注意:楽天は条件付き UI でログインを呼ぶため、拡張を有効にしているとマイナ側に処理が向きます。普段の通常ログインを邪魔しないよう、使わない時は拡張をオフに。

限界はありつつも、マイナンバーカードが、実在の金融機関で「使えるパスキー」になったのは大きな一歩でした。次はリレーを BLE 化して近接性を担保し、より実用に近づけていきます。

コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

IP: 取得中...
216.73.216.139216.73.216.139