復旧と端末
パスキーの復旧(アカウントリカバリー)の設計:端末をなくした利用者をどう戻すか
8 分で読めます
この記事の要点
- 復旧の入口は、ログインの入口と同じだけ狙われます。復旧が弱いと、パスキーで固めたログインも復旧の強さまで下がります。
- 設計は「なくさない工夫」「自分で戻せる方法」「窓口での確認」の 3 段で考え、復旧の後は必ず利用者に知らせます。
- なくした端末のパスキーは失効し、新しい端末で登録し直してもらいます。
パスキーの復旧は、パスキーを使えなくなった利用者が、別の方法で本人であることを示し、新しいパスキーを登録し直す流れです。設計は、(1) そもそもなくさないようにする、(2) 利用者が自分で戻せる方法を用意する、(3) それでも戻れない人を窓口で確認する、の 3 段で考えます。復旧の入口はログインと同じだけ狙われるので、復旧の方法がログインより弱いと、全体の強さは復旧の強さまで下がります。復旧が済んだら利用者に知らせ、なくした端末のパスキーは失効します。
復旧がなぜ難しいのか
パスキーのログインは、秘密鍵を持つ端末が無ければ通りません。これは安全の理由そのものですが、端末をなくした本人も同じく通れなくなります。
そこでパスキー以外の方法で本人を確かめることになりますが、攻撃者もその方法を狙います。たとえば、ログインはパスキーでしかできないのに、「メールのリンクを開けばパスキーを登録し直せる」復旧があれば、攻撃者はメールアカウントを乗っ取るだけで新しいパスキーを登録できます。パスキーのフィッシング耐性は、ここで失われます。
復旧を設計するときの問いは、「この方法で戻れるのは本人だけか」と「その方法は、ログインで求めている強さと釣り合っているか」の 2 つです。
1 段目:なくさない工夫
復旧が要る場面を減らすのが、最も効果があります。
同期パスキーを使ってもらう
同期パスキーは、OS やパスワードマネージャーのクラウドを通じて、利用者の他の端末にも入ります。Apple は、iCloud キーチェーンで同期したパスキーについて、利用者がすべての端末をなくしても復元できるよう設計していると説明しています。Google も、Google パスワードマネージャーが Android と Chrome の間でパスキーを同期すると説明しています。1 台を失っても、同じアカウントの別の端末でログインできます。
2 本目のパスキーを勧める
WebAuthn の認証器のデータには、そのパスキーが同期できる種類か (BE、Backup Eligibility) と、いまバックアップされているか (BS、Backup State) のフラグがあります。登録したパスキーがすべて同期されていない利用者には、別の端末やセキュリティキーで 2 本目を登録するよう勧めます。NIST SP 800-63B-4 も、複数の独立した認証手段を持たせることを、1 つを失っても戻れるようにする方法として挙げています。
2 段目:自分で戻せる方法
NIST SP 800-63B-4 は、アカウントの復旧の方法として次のようなものを挙げています。
| 方法 | 内容 | 気を付けること |
|---|---|---|
| 保存しておく復旧コード | 登録のときに渡し、利用者が控えておく | 十分な長さの乱数にし、ハッシュで保存し、1 回限りにする |
| 発行する復旧コード | 登録済みの連絡先に、その都度コードを送る | 連絡先 (メールなど) の乗っ取りがそのまま復旧の乗っ取りになる |
| 復旧の連絡先 | 利用者があらかじめ指定した信頼できる人に送る | 個人向けのサービスでは運用が重くなりやすい |
| 本人確認のやり直し | 最初の登録と同じ水準の本人確認をもう一度行う | 費用と時間がかかる |
1 つの方法だけで足りないときは、組み合わせます。たとえば「登録済みのメールに届くコード」に加えて「最近使った端末からの操作である」「支払い情報の一部が一致する」などを求めれば、メールの乗っ取りだけでは戻れなくなります。どの組み合わせが妥当かは、サービスが扱う情報や金銭の重さで決まります。
3 段目:窓口での確認
自分で戻せない利用者は、窓口で確認します。ここは攻撃者にとっても狙い目なので、手順を文書にし、担当者の判断に頼らないようにします。
- 確認に使う情報を決めておく (登録時の情報、取引の履歴、本人確認書類など)
- 窓口で「パスキーを解除する」のではなく、「新しいパスキーを登録できる短い期限付きの権利を出す」形にする
- 誰が、いつ、どの理由で復旧を許可したかを記録する
復旧の後にやること
利用者に知らせる
NIST SP 800-63B-4 は、復旧が行われたときに利用者へ通知することを求めています。本人が知らないうちに復旧されていれば、乗っ取りに気付く手がかりになります。登録済みの連絡先すべてに送るのが基本です。
なくした端末のパスキーを失効する
なくした端末のパスキーは、拾った人がその端末のロックを解けない限り使えませんが、残しておく理由もありません。利用者が「どの端末をなくしたか」を選んで失効できるよう、端末の一覧に名前と最後に使った日を出しておきます。乗っ取りが疑われる場合は、すべてのパスキーを失効してから登録し直してもらいます。
セッションを見直す
復旧の時点で生きているセッションを無効にするかどうかを決めます。乗っ取りが疑われる経緯での復旧なら、すべて無効にします。
公開前に決めておくこと
- 自分で戻せる方法は何か。その方法はログインと釣り合っているか
- 窓口で何を確認するか。誰が許可し、どこに記録するか
- 復旧の後に、誰に、何で知らせるか
- 失効は 1 本ずつか、すべてか
SealGate で実装する場合
SealGate は本人確認をしません。本人確認は開発者側で済ませ、その後に SealGate の API で再登録の権利を出します。
# 1. 開発者側で本人確認をした後、復旧トークンを発行する
curl -X POST https://api.sealgate.jp/v1/recovery/start \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{"external_id":"user-123","reason":"窓口で本人確認"}'
# → { "recovery_token": "rt_...", "expires_at": "..." } (15 分有効)- 開発者のサーバーは、その利用者の登録の options を取るときに
recovery_tokenを付けてPOST /v1/register/optionsを呼びます。ブラウザ側は通常の登録と同じく SDK のsg.register()です。 - 既にパスキーを持つ利用者には、
signed_in: trueかrecovery_tokenが無いと登録できません (403recovery_not_allowed)。復旧トークンが無効か期限切れの場合も同じです。
復旧の前後で使える API です。
GET /v1/users/{external_id}/credentials: パスキーの一覧。name・last_used_at・backup_eligible・backed_upが返るので、なくした端末の特定や、2 本目を勧める判断に使えます。DELETE /v1/credentials/{credential_id}: なくした端末のパスキーを 1 本失効します。POST /v1/users/{external_id}/revoke: 乗っ取りが疑われるときに、その利用者のパスキーをすべて失効します (利用者と監査ログは残ります)。- Webhook の
recovery.issued: 復旧トークンの発行を受け取れます。利用者への通知を送るきっかけに使えます。reasonは監査ログに残ります。
詳しくは ドキュメントの復旧の節 をご覧ください。
よくある質問
同期パスキーなら復旧の仕組みは要りませんか?
要ります。同期を使っていない利用者、端末固定のパスキーだけの利用者、同期のアカウント自体に入れなくなった利用者がいるためです。
メールのリンクだけで復旧させてよいですか?
メールアカウントを乗っ取られると、そのまま復旧も乗っ取られます。扱う情報が重いサービスでは、メールに別の要素を組み合わせるか、窓口での確認を求めます。
復旧の後、古いパスキーは自動で消えますか?
自動では消えないものとして設計し、なくした端末のパスキーを利用者か運用者が失効できるようにしておきます。