パスキー認証の技術と実装

Tech

本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。

パスキー認証の技術と実装

パスキーは、FIDO Allianceが提唱するWebAuthn標準に基づいた次世代の認証技術です。パスワードレスでありながら、フィッシング耐性やMFA(多要素認証)特性を兼ね備える強力なソリューションとして注目されています。しかし、その実装と運用には、プロトコルの深い理解とセキュリティプラクティスの徹底が不可欠です。

脅威モデル

パスキーはパスワードに比べて多くの脅威に耐性がありますが、完全に万能ではありません。実務家としては、残存する脅威を正確に把握し、適切な対策を講じる必要があります。

  1. Relying Party (RP) 側の脆弱性:

    • サーバーサイドの脆弱性: RPサーバーのXSS、CSRF、SQLインジェクションなどにより、FIDO登録/認証フローがハイジャックされる可能性。

    • 実装の不備: WebAuthnプロトコルのChallenge検証、Origin検証、Attestation検証の不備。特にOrigin検証の欠如はフィッシングサイトでの不正なパスキー登録を許す原因となります。

    • セッション管理の脆弱性: パスキー認証後のセッションがハイジャックされた場合、アカウントが乗っ取られる。

  2. ユーザー側の脅威:

    • デバイスの物理的盗難: ロック解除されたデバイスが悪用され、パスキーが不正に利用される可能性。

    • キープロバイダの脆弱性: OS、ブラウザ、TPMなどの基盤技術に脆弱性が見つかった場合、パスキーが不正にアクセスされるリスク。

    • ソーシャルエンジニアリング: ユーザーが意図せず、パスキーの削除や不正なパスキーの登録を承認させられる可能性。

  3. パスキープロバイダ (Authenticator) の脅威:

    • クラウド同期の脆弱性: GoogleやAppleなどのパスキープロバイダが提供するクラウド同期機能に脆弱性があった場合、同期されたパスキーが漏洩するリスク。

攻撃シナリオ

以下に、RP側の実装不備を悪用したパスキーの不正登録によるアカウント乗っ取りシナリオをMermaid attack chainで可視化します。

flowchart LR
    A["攻撃者"] -->|1. RPサーバへのCSRF/XSS攻撃| B("RPサーバの脆弱性")
    B -->|2. ユーザーのセッションハイジャック| C["被害ユーザーのセッション"]
    C -->|3. ユーザーに偽のFIDO登録ページを表示| D("フィッシングサイト/改ざんされたRPサイト")
    D -->|4. RPのWebAuthn APIに不正リクエスト| E["RPサーバ(WebAuthn API)"]
    E -->|5. ユーザーにパスキー登録を要求| F["被害ユーザーのデバイス"]
    F -->|6. ユーザーが不正なパスキーを登録| E
    E -->|7. Origin検証/Challenge検証不備により許可| G["RPサーバの認証情報DB"]
    G -->|8. 不正なCredential IDが登録完了| A
    A -->|9. 登録したパスキーでアカウント乗っ取り| G

解説: このシナリオでは、RPサーバーの脆弱性(XSSやCSRF)を攻撃者が悪用し、正規ユーザーのセッションを乗っ取ります。その後、ユーザーを誘導して、RPのWebAuthn APIに対して不正なパスキー登録リクエストを送信させます。RPサーバー側でOriginやChallengeの厳格な検証が欠如している場合、この不正なパスキーが登録されてしまい、攻撃者はそのパスキーを使って正規ユーザーのアカウントを乗っ取ることができます。

検出/緩和

検出

  • RPログ監視: FIDO登録/認証リクエストのOrigin不一致、Challenge検証失敗、Attestation検証失敗などのログをリアルタイムで監視。異常なリクエスト頻度や地域からのアクセスも異常検知のトリガー。

  • ユーザーへの通知: 新規パスキー登録時やパスキー削除時に、登録済みのメールアドレスや電話番号に即座に通知を送信し、ユーザー自身に確認を求める。

  • 異常な振る舞い検知: ユーザーの行動パターン(ログイン頻度、アクセス元IP、利用デバイス)から逸脱したFIDO操作を検知する。

緩和

1. 鍵/秘匿情報の取り扱い

  • Credential IDの管理: Credential ID自体は公開情報ですが、RPのユーザーDBとの紐付け情報は秘匿すべきです。RP側でCredential IDが漏洩しても直接認証に悪用はできませんが、ユーザーを特定した標的型攻撃に悪用される可能性があります。データベースでは適切に暗号化し、最小権限の原則に基づいてアクセスを制限します。

  • Attestation Root Certificates: FIDOアテステーション検証に用いる信頼できるルート証明書(Trust Anchor)は、FIDO Allianceが公開している情報を定期的に更新し、信頼できるソースからのみ取得します。これらは公開情報ですが、改ざんされて偽の認証器を信頼しないよう、RPサーバ内で厳重に管理します。

  • 鍵ローテーション: パスキー自体はユーザーのデバイスに紐づくため、RP側で「鍵ローテーション」という概念は直接適用されません。しかし、セキュリティポリシーとして、一定期間ごとにパスキーの再登録を促す、または特定のパスキーを無効化し、ユーザーに新しいパスキーの登録を求める運用は可能です。

  • 最小権限: FIDO関連APIへのアクセス権限は最小限に絞り込み、不要なシステムやユーザーからのアクセスを禁止します。

  • 監査: すべてのFIDO登録、認証、削除イベントについて詳細な監査ログを記録し、定期的にレビューできる体制を構築します。

2. 暗号/プロトコル誤用例と安全な代替

WebAuthnプロトコルの主要なセキュリティ機能は、RP側での厳格な検証にあります。特にOriginとChallengeの検証は不可欠です。

誤用例 (Python WebAuthnライブラリの簡略化された例) OriginとChallengeの検証が不十分な場合、攻撃者が不正なFIDO登録を成功させるリスクがあります。

# BAD: Origin検証が不十分、またはChallengeが使い回される例

from webauthn import verify_registration_response
from webauthn.helpers.exceptions import InvalidRegistrationResponse

def bad_verify_registration(attestation_object_bytes, client_data_json):
    try:

        # rp_idとoriginがハードコードされているか、入力値が十分に検証されない


        # challengeがサーバサイドで適切に生成・保持されていない

        verified_registration = verify_registration_response(
            attestation_object=attestation_object_bytes,
            client_data_json=client_data_json,
            rp_id="your-rp.example.com", # RP IDは固定だが、Originがクライアントから送られたものをそのまま使うなど
            origin="https://your-rp.example.com", # これがクライアントデータと厳密に比較されない
            challenge=b'constant_challenge_value', # 固定または使い回されるチャレンジ

            # ... その他の設定 ...

        )
        print("Registration successful but potentially insecure.")
        return True
    except InvalidRegistrationResponse as e:
        print(f"Registration verification failed due to: {e}")
        return False

# このような実装では、攻撃者が中間者攻撃やCSRFでoriginやchallengeを偽装した場合に脆弱になる。

安全な代替 (Python WebAuthnライブラリの例) OriginとChallengeを厳格に検証することが安全な実装の鍵です。

# GOOD: OriginとChallengeを厳格に検証する例

from webauthn import verify_registration_response
from webauthn.helpers.exceptions import InvalidRegistrationResponse
import os

# サーバサイドで期待されるRP IDとOriginを定義

EXPECTED_RP_ID = "your-rp.example.com"
EXPECTED_ORIGIN = "https://your-rp.example.com"

def good_verify_registration(attestation_object_bytes, client_data_json, stored_challenge: bytes):
    """
    パスキー登録応答を安全に検証する関数。
    :param attestation_object_bytes: クライアントから送信されたattestationObjectのバイト列
    :param client_data_json: クライアントから送信されたclientDataJSONのバイト列
    :param stored_challenge: サーバサイドで生成し、セッションに保持していたチャレンジ値
    """
    try:
        verified_registration = verify_registration_response(
            attestation_object=attestation_object_bytes,
            client_data_json=client_data_json,
            rp_id=EXPECTED_RP_ID,
            origin=EXPECTED_ORIGIN,
            challenge=stored_challenge, # サーバサイドで生成し、一度しか使わないチャレンジ

            # attestation_statement_fmt, trust_anchors, etc. も適切に設定する

        )

        # 追加のチェック: clientDataJSON内のOriginが期待されるものと一致するか明示的に確認

        if verified_registration.registration_response.client_data.origin != EXPECTED_ORIGIN:
            raise InvalidRegistrationResponse("Client data origin mismatch.")

        # Challegeが使い捨てであるかを確認 (stored_challenge をセッションから削除など)


        # remove_challenge_from_session(stored_challenge)

        print(f"Registration successful for credential ID: {verified_registration.credential_id.hex()}")
        return True
    except InvalidRegistrationResponse as e:
        print(f"Secure registration verification failed: {e}")
        return False

# 登録フロー例:


# 1. ユーザーが登録開始 -> サーバがos.urandom(32)でチャレンジ生成し、セッションに保存


# 2. クライアントがパスキー登録リクエストを送信


# 3. good_verify_registration(..., stored_challenge=session['challenge']) を呼び出す

この例では、RP ID、Origin、そしてサーバサイドで生成され一度しか使われないChallengeを厳格に検証することで、フィッシングやリプレイ攻撃への耐性を高めています。

運用対策

  • 多要素認証リカバリー: パスキー自体がMFAの特性を持ちますが、パスキーを紛失・盗難した場合の安全なリカバリープロセスが必須です。別のMFA手段(例:ハードウェアセキュリティキー、ワンタイムパスワードアプリ)、または本人確認を伴う厳格な手続きを用意します。

  • ユーザー教育: パスキーのフィッシング耐性について誤解を与えず、それでもなお不審なサイトでパスキーの操作(登録、削除など)を求められた場合の注意喚起を行います。

  • ソフトウェアの最新化: RP側のWebAuthnライブラリ、OS、ブラウザ、FIDOセキュリティキーのファームウェアを常に最新に保ち、既知の脆弱性に対応します。

  • ログと監査の強化: FIDO関連の全イベント(登録、認証、削除)について詳細なログを取得し、SIEMシステムなどと連携して異常検知と監査を継続的に実施します。

現場の落とし穴

  • 誤検知と検出遅延: 不正なFIDO操作を検知するための閾値設定は困難です。厳しすぎると正規ユーザーの操作がブロックされ、緩すぎると攻撃を見逃します。リアルタイムでの検知と対応には高度な分析能力が求められます。

  • 可用性トレードオフ: 厳格なFIDO検証はセキュリティを高めますが、不適切な実装や過度な検証は正規ユーザーの認証を妨げ、可用性を損ねる可能性があります。特にアカウントリカバリーフローの複雑さはユーザー体験に直結します。

  • キープロバイダへの過信: パスキープロバイダ(OS/ブラウザ/TPMなど)のセキュリティ基盤が堅牢であると仮定しすぎると、その基盤に脆弱性が見つかった際のリスク評価と対応が遅れる可能性があります。常に最悪のシナリオを考慮し、多層防御の視点を持つべきです。

  • パスキー同期のセキュリティ: パスキープロバイダによるパスキーのクラウド同期は利便性が高い反面、プロバイダ側のシステムが攻撃された場合、ユーザーの複数のサービスに影響が及ぶ可能性があります。このリスクはRP側では直接制御できませんが、ユーザーへの情報提供や代替リカバリー手段の強化で緩和できます。

まとめ

パスキーは、現代の認証における最も堅牢な選択肢の一つです。しかし、その強力なセキュリティ特性を最大限に引き出すためには、RP側の厳格なプロトコル実装と継続的な運用努力が不可欠です。本記事で述べた脅威モデル、攻撃シナリオ、そして具体的な検出・緩和策、運用対策を理解し、現場の落とし穴を回避することで、安全なパスキー認証システムを構築・維持できるでしょう。

ライセンス:本記事のテキスト/コードは特記なき限り CC BY 4.0 です。引用の際は出典URL(本ページ)を明記してください。
利用ポリシー もご参照ください。

コメント

タイトルとURLをコピーしました