代表的な面接トピック

一般面接:フィッシング耐性のあるパスキーログインおよびリカバリフローをどのように設計しますか?

一般難しい
Offer.cc 編集チーム公開日 更新日

質問

マルチデバイスSaaS向けのパスキーログインフローを設計してください。登録および認証チャレンジのライフサイクル、RP IDとオリジンの検証、UP/UVフラグの処理、同期可能クレデンシャルのポリシー、そしてすべてのパスキーを紛失した後にセキュリティをSMSコードへと低下させずにアカウントを回復する方法について説明してください。

プロンプトと適切なコンテキスト

対象のSaaSはすでにパスワードをサポートしており、新たにパスキーを追加しようとしています。ユーザーはスマートフォン、ノートPC、またはハードウェアセキュリティキーからサインインする可能性があり、一部のクレデンシャルはプラットフォームを介して同期される一方で、リスクの高いエンタープライズアクションにはより強力なユーザー検証が必要です。登録、認証、リカバリ、移行、およびオブザーバビリティを設計してください。

スコープはWebAuthnとサーバー検証の境界です。パスキーはセッション管理、アカウントリカバリ、デバイス失効、またはビジネス認可を自動的に解決するものではありません。

面接官が評価するポイント

面接官は、ブラウザと認証器(authenticator)が秘密鍵を保持し、サーバーが公開鍵を保存すること、各セレモニーが1回限りのランダムなチャレンジを使用すること、そして検証側がチャレンジ、RP ID、オリジン、署名、および必要なユーザー存在(UP)またはユーザー検証(UV)フラグを検証することを理解しているかを確認します。

優れた回答では、「フィッシング耐性があること」と「決して失われないこと」も明確に区別されます。同期可能なクレデンシャルは可用性を向上させますが、最も厳格な非エクスポート性要件を満たさない場合があります。リカバリが同等の証明をバイパスしてしまうと、設計全体の強度は最も脆弱なパスと同等になってしまいます。

最初に確認すべき明確化の質問

  • サインインのみを必要とするアクションと、UVやデバイスバインド型クレデンシャルを必要とするアクションはどれか?
  • RP IDは複数のサブドメインを対象としているか、またWebAuthnはクロスオリジンiframe内で実行されるか?
  • エンタープライズ側で同期可能クレデンシャルを禁止したり、ハードウェアキーを必須としたりすることは可能か?
  • リカバリのためにどの既存要素が残り、オペレーターによる審査は可能か?
  • パスワード、TOTP、パスキーの移行および失効タイムラインはどのようになっているか?

30秒の回答フレームワーク

「サーバーは登録または認証ごとに1回限りのチャレンジを生成し、セッションにバインドします。セレモニー後、RP ID、オリジン、チャレンジ、署名、および必要なUP/UVフラグを検証します。登録時には公開鍵、クレデンシャルID、カウンター、およびポリシーメタデータを保存し、秘密鍵は決して保存しません。リスクに応じて同期可能クレデンシャルを階層化し、機密性の高いアクションにはUVまたはデバイスバインディングを要求します。リカバリには既存の強力な要素、短期間のワンタイムフロー、およびリスクレビューを使用し、SMSを無条件のバックドアにはしません。」

ステップバイステップの詳細な回答

ステップ1:登録および認証データを定義する

登録の前に、サーバーは予測不可能で有効期間の短いワンタイムチャレンジを生成し、セッションまたはワンタイムストアに保存します。クライアントがnavigator.credentials.create()を呼び出し、認証器がキーペアを生成して公開鍵クレデンシャルを返します。

必要に応じて、クレデンシャルID、公開鍵、アカウント、RP ID、署名カウンター、バックアップ適格性(backup eligibility)、およびバックアップ状態(backup state)を保存します。秘密鍵は認証器またはプラットフォームのクレデンシャルマネージャー内に保持されます。

ステップ2:厳格な認証チェックを実行する

サインインの場合、サーバーは新しいチャレンジとpublicKeyCredentialRequestOptionsを生成し、クライアントはnavigator.credentials.get()を呼び出します。以下を検証します:

ts
const valid = await verifyAuthenticationResponse({
  response,
  expectedChallenge: session.challenge,
  expectedOrigin: "https://app.example.com",
  expectedRPID: "app.example.com",
  requireUserVerification: true
});

検証が成功したら直ちにチャレンジを消費(破棄)します。リプレイされたレスポンス、期限切れのレスポンス、またはクロスセッションのレスポンスを拒否し、保存されている公開鍵で署名を検証します。また、キャンセル、サポートされていないブラウザ、および一時的に利用できない認証器も処理します。

ステップ3:RP ID、オリジン、およびクロスオリジンのリスクを理解する

RP IDはクレデンシャルの依拠当事者(relying party)を識別します。クレデンシャルは異なるRP IDに対して認証することはできません。また、サーバーは登録可能ドメインの比較だけでなく、呼び出し元オリジンもチェックする必要があります。クロスオリジンiframeまたはRelated Origin Requestでは、ユーザーが誰がセレモニーを要求しているかを認識しているかどうかの明示的なレビューと、個別の互換性テストが必要です。

会社のロゴはオリジンの証明にはなりません。オリジン、RP ID、チャレンジ、および署名は、ブラウザ/認証器のコンテキストとサーバーにわたってまとめて検証される必要があります。

ステップ4:リスクに応じてUP、UV、および同期状態を判断する

UPはユーザーが認証器を操作したことを意味し、UVは認証器がユーザーをローカルで検証したことを意味します。通常のサインインではリスクに応じてポリシーを選択できますが、送金、キーのエクスポート、管理者アクションではUVを必須とし、サーバー側で返されたフラグを検査する必要があります。

同期可能なクレデンシャルはデバイス間の可用性を向上させますが、NISTは同期がキーのエクスポート可能性を伴うことを指摘しています。それが保証レベルを満たすかどうかは、導入ポリシーに依存します。バックアップの適格性と状態を記録し、「同期可能」を「すでに同期済み」や「デバイスバインド」と呼んではいけません。

ステップ5:リカバリ、移行、および失効を設計する

ユーザーがデバイスを紛失した場合は、別の登録済みパスキー、エンタープライズリカバリキー、または審査を伴う強力な本人確認フローを優先します。リカバリトークンは短寿命、ワンタイム、セッションバインド、かつ失効可能でなければなりません。リカバリ後は、ユーザーに通知し、セッションをローテーションし、ユーザーが古いクレデンシャルを確認して削除できるようにします。

パスワード移行中は、管理された移行期間を維持し、パスキー作成後はパスワードへの依存度を下げます。同一リクエスト内ですべての古い要素をサイレントに削除してはいけません。各クレデンシャルには個別の失効状態が必要であり、異常なカウンター、リスクの高いデバイス、およびリカバリイベントは監査ストリームに記録します。

高品質な回答サンプル

「私はまずセキュリティ目標を階層化することから始めます。サーバーは登録および認証ごとに1回限りのチャレンジを生成し、短寿命のセッションに保存します。そのアクションで要求されるチャレンジ、RP ID、オリジン、署名、およびUP/UVフラグを検証し、その後チャレンジを消費します。データベースには公開鍵、クレデンシャルID、カウンター、アカウント、およびバックアップ状態を保存します。秘密鍵が認証器から出ることは決してありません。

クロスオリジンiframeの使用はデフォルトでは有効にしません。必要な場合は、呼び出し元オリジン、トップレベルオリジン、およびRP IDをテストマトリクスに含めます。通常のサインインではポリシーに準拠した同期可能クレデンシャルを受け入れることができますが、機密性の高いアクションにはUVまたはデバイスバインディングを要求します。リカバリでは別の強力なパスキーまたはエンタープライズリカバリキーを優先し、ワンタイムの有効期限、リスク制御、および通知を設けます。SMSは明示的に合意されたフォールバックとすることはできますが、強力な認証を永続的に回避する手段にしてはなりません。本番テレメトリでは、チャレンジのリプレイ、オリジンの不一致、UVの欠落、リカバリの成功、および異常な失効を監視します。」

よくある間違い

  • 署名のみを検証する → 有効な署名であっても、正しいチャレンジ、RP ID、またはオリジンであることは証明されません → 各フィールドをチェックし、チャレンジを消費する。
  • UPをUVとして扱う → 認証器へのタッチはローカルのユーザー検証ではありません → 高リスクのアクションにはUVを要求して検査する。
  • 同期可能なパスキーをデバイスバインドと呼ぶ → 同期適格性と実際の同期は異なる事実です → バックアップフラグを記録し、保証レベルに基づいて決定する。
  • 即座にSMSリンクでリカバリする → 最も脆弱なパスによってアカウントが乗っ取られる可能性があります → 既存の強力な要素、短寿命トークン、リスクレビュー、および通知を使用する。
  • 登録可能ドメインのみをチェックする → オリジンにはスキーム、ホスト、ポートが含まれます → 正規化された完全なオリジンを比較する。
  • チャレンジを再利用する → 傍受されたアサーションがリプレイされる可能性があります → チャレンジはランダム、短寿命、ワンタイム、かつセッションバインドにする。

フォローアップの質問と回答

フォローアップ1:なぜパスキーはフィッシング耐性があるのですか?

認証器はオリジンとRP IDに基づいてクレデンシャルを選択し、そのコンテキストを付与してサーバーチャレンジに署名します。フィッシングサイトは、認証器から本物のサービスに対する有効な証明を取得することはできません。ただし、サーバーは依然としてオリジン、RP ID、およびチャレンジを検証する必要があります。

フォローアップ2:チャレンジをクライアント側のみに保持してはいけないのはなぜですか?

サーバーは、レスポンスを現在のログイン意図に関連付け、リプレイを拒否するために、自身が発行したランダムな値を把握している必要があります。クライアントが生成した値や個別に保存された値では、サーバーがこのセレモニーを開始したことを証明できません。

フォローアップ3:同期可能なクレデンシャルは本質的に安全ではないのですか?

二者択一の主張は避けるべきです。同期はリカバリや複数デバイス間での使いやすさを向上させる一方で、エクスポート可能性、プロバイダーのポリシー、組織の保証要件は異なります。サーバーはリスク層に応じてバックアップフラグを検査し、機密性の高いアクションにはより強力な要素を要求する必要があります。

フォローアップ4:ユーザーがすべてのデバイスを紛失した場合はどうなりますか?

リカバリを高リスクの認証として扱います。エンタープライズリカバリキー、審査を経た別の強力な要素、またはオペレーターによるレビューを使用し、スコープと時間を限定し、通知を行い、不明なクレデンシャルを失効させます。利便性によってログインの保証レベルを永続的に低下させてはなりません。

公開情報ソース

関連する質問