implementing-hardware-security-key-authentication について
WebAuthnを使ったFIDO2セキュリティキーとパスキー認証の実装ガイドです。サーバー側の認証手順、キー登録、移行、復旧を扱います。
主な機能
- 01WebAuthn依拠当事者の設定
- 02資格情報の登録と検証
- 03YubiKeyの登録と管理
- 04パスキー移行と復旧の計画
活用シーン
- 01Webサービスへのフィッシング耐性MFA追加
- 02ユーザー名不要のパスキー認証
- 03主キーと予備キーの登録
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ mukul975/ anthropic-cybersecurity-skills --skill implementing-hardware-security-key-authenticationインストール後、Claude Code で/implementing-hardware-security-key-authenticationを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
LICENSEreferences/api-reference.mdscripts/agent.py詳細
ハードウェアセキュリティキー認証の実装
使用するタイミング
- 高価値アカウント(管理者、開発者、特権ユーザー)向けに、FIDO2 ハードウェアセキュリティキーを使ったフィッシング耐性のある多要素認証(MFA)を導入する場合
- ローミングオーセンティケーター(USB/NFC セキュリティキー)とプラットフォームオーセンティケーター(Windows Hello、Touch ID、Android の生体認証)の両方をサポートする WebAuthn リライイングパーティーサーバーを構築する場合
- 既存のパスワードベース認証システムを、パスキー(検出可能なクレデンシャル)を主要または副次的な認証要素としてサポートするように移行する場合
- 組織の従業員向けに YubiKey デバイスを登録する場合(PIN の設定、クレデンシャルの登録、バックアップキーのプロビジョニングを含む)
- NIST SP 800-63B AAL3(オーセンティケーター保証レベル 3)の要件に準拠したパスワードレス認証フローを実装する場合
本番環境で HTTPS なしに使用しないでください(WebAuthn にはセキュアなオリジンが必要です)。また、ユーザーが USB/NFC ポートに物理的にアクセスできないシステムや、紛失したキーに対する復旧手段なしで唯一の認証要素として使用する場合にも適していません。
前提条件
- Python 3.10 以降に、
fido2(python-fido2 >= 2.0.0)、flask、cryptographyライブラリがインストールされていること - HTTPS が有効な Web サーバー(WebAuthn API はセキュアコンテキストを必要とします。開発用途では localhost が例外として許可されます)
- FIDO2 互換のハードウェアセキュリティキー(YubiKey 5 シリーズ、SoloKeys、Titan Security Key)またはプラットフォームオーセンティケーター
- WebAuthn API をサポートする最新のWebブラウザー(Chrome 67 以降、Firefox 60 以降、Safari 14 以降、Edge 79 以降)
- 公開鍵暗号、チャレンジレスポンスプロトコル、HTTP セッション管理の理解
ワークフロー
ステップ 1: リライイングパーティーサーバーの設定
WebAuthn リライイングパーティー(RP)の ID とサーバーを設定します。
- RP ID の定義: リライイングパーティー名(ユーザーに表示される表示名)と RP ID(アプリケーションの実効ドメイン)を指定して
PublicKeyCredentialRpEntityを作成します。RP ID はオリジンの登録可能なドメインサフィックスである必要があります。たとえばexample.comはhttps://auth.example.comに対して有効ですが、other.comは無効です。 - Fido2Server の初期化: python-fido2 ライブラリの
Fido2Serverクラスを、RP エンティティを渡して生成します。サーバーはチャレンジの生成、アテステーションの検証、アサーションの検証を処理します。 - アテステーション設定の指定: サーバーがオーセンティケーターの身元証明を要求するかどうかを制御するため、アテステーションの伝達(conveyance)設定を指定します。
none: アテステーションを要求しません(最もシンプルで、ほとんどの導入に推奨されます)indirect: アテステーションが提供されることがありますが、CA によって匿名化される場合がありますdirect: オーセンティケーターの製造元からの完全なアテステーションチェーンenterprise: 管理対象環境向けのデバイス識別アテステーション
- セッション管理: 登録・認証のセレモニーの開始フェーズと完了フェーズの間で WebAuthn の状態を保存するため、サーバー側セッションを設定します。SameSite=Strict を指定し、secure かつ httponly の Cookie を使用してください。
- クレデンシャルの保存: クレデンシャルのレコードを保存するデータベーススキーマを設計します。項目は
credential_id(バイナリ)、public_key(COSE キー)、クローン検出用のsign_count(uint32)、user_id、created_at、last_used、display_name、transports(USB、NFC、BLE、internal)です。
Step 2: 登録セレモニー(クレデンシャルの作成)
新しいクレデンシャルを作成するために、WebAuthn の登録フローを実装します。
- 登録の開始: ユーザーエンティティ(ユーザー ID、ユーザー名、表示名を含む
PublicKeyCredentialUserEntity)、そのユーザーの既存クレデンシャルの一覧(重複登録を防ぐため)、およびuser_verificationとauthenticator_attachmentのオプションを指定して、server.register_begin()を呼び出します。 - オーセンティケーターの選択基準:
authenticator_attachment: cross-platformは、ローミングオーセンティケーター(USB/NFC キー)に限定しますauthenticator_attachment: platformは、内蔵オーセンティケーター(Touch ID、Windows Hello)に限定します- このフィールドを省略すると、両方の種類が許可されます
resident_key: requiredは、オーセンティケーターに保存される検出可能なクレデンシャル(パスキー)の作成を強制しますuser_verification: requiredは、オーセンティケーターでの PIN または生体認証による検証を強制します
- クライアント側のセレモニー: ブラウザーはサーバーから受け取ったオプションを使って
navigator.credentials.create()を呼び出します。オーセンティケーターは新しい鍵ペアを生成し、秘密鍵をセキュアエレメントに保存して、公開鍵、クレデンシャル ID、アテステーションオブジェクト、およびクライアントデータ JSON を返します。 - 登録の完了: 保存された状態とクライアントのレスポンスを指定して、
server.register_complete()を呼び出します。サーバーはアテステーション署名を検証し、クレデンシャルの公開鍵と ID を取り出して、保存すべきクレデンシャルデータを含むAuthenticatorDataを返します。 - クレデンシャルの保存:
credential_data(credential_id、COSE 鍵形式のpublic_key、およびsign_countを含む)を、そのユーザーアカウントに関連付けてデータベースに保存します。
Step 3: 認証セレモニー(アサーション)
クレデンシャルを検証するために、WebAuthn の認証フローを実装します。
- 認証の開始: そのユーザーの登録済みクレデンシャルの一覧を指定して
server.authenticate_begin()を呼び出します(オーセンティケーターがユーザーを特定する検出可能なクレデンシャルのフローでは省略します)。必要な保証レベルに応じてuser_verificationを設定します。 - クライアント側のアサーション: ブラウザーはサーバーのオプションを使って
navigator.credentials.get()を呼び出します。オーセンティケーターは一致するクレデンシャルを探し、必要に応じてユーザー検証を実行し、署名カウンターを増やして、秘密鍵でチャレンジに署名します。 - 認証の完了: 保存された状態、登録済みクレデンシャル、およびクライアントのレスポンスを指定して
server.authenticate_complete()を呼び出します。サーバーは保存された公開鍵に対してアサーション署名を検証し、署名カウンターが増加していることを確認します(クローン検出)。 - sign count の更新: 認証に成功したら、そのクレデンシャルの保存済み
sign_countを更新します。新しい sign count が保存値より大きくない場合、鍵がクローンされた可能性があるため、セキュリティイベントとして扱ってください。 - 検出可能なクレデンシャルのフロー: パスワードレス認証では、ユーザーは最初にユーザー名を入力する必要がありません。オーセンティケーターは RP ID に対応するすべての検出可能なクレデンシャルを提示し、選択されたクレデンシャルの
userHandleによってユーザーが特定されます。
ステップ 4: YubiKey の登録と管理
組織向けの YubiKey プロビジョニングワークフローを実装します:
- PIN の初期化: 初回使用の前に、YubiKey には FIDO2 PIN が必要です(最小 4 文字、ロックアウトまでの再試行回数は 8 回)。Yubico Authenticator アプリケーションを使用するか、CTAP2 の
clientPinコマンドをプログラムから呼び出して、ユーザーの PIN 設定を案内します。 - プライマリキーの登録: ユーザーのプライマリ YubiKey をアカウントに登録します。わかりやすいラベル(例: "USB-A YubiKey - Office")と、デバイス識別用のオーセンティケーターの AAGUID を併せて資格情報を保存します。
- バックアップキーの登録: ユーザーに少なくとも 2 本のセキュリティキーを登録することを求めます。バックアップキーは別の場所(自宅、貸金庫など)に保管すべきです。どちらのキーでも認証できるよう、両方のキーを同じアカウントに登録する必要があります。
- キーアテステーションの検証: エンタープライズ環境では、アテステーション証明書チェーンを検証して、そのキーが Yubico 製の正規の YubiKey であることを確認します。AAGUID を Yubico が公開している値と照合し、正確なモデルを特定します。
- キーのライフサイクル管理: ユーザーが登録したキーの一覧表示、侵害されたキーの失効、再登録の強制、キー使用パターン(最終認証日時、総認証回数)の監査を行う管理機能を実装します。
ステップ 5: パスキー移行戦略
パスワードからパスキーへの移行を計画し、実行します:
- 段階的な展開: 既存のパスワード認証と並行して、任意のパスキー登録から始めます。採用状況の指標(パスキーを持つユーザーの割合、パスワードではなくパスキーでログインした割合)を追跡します。
- 資格情報のアップグレードフロー: ユーザーがパスワードで認証したときに、パスキーの登録を促します。手間を最小限に抑えるため、パスワードログイン成功直後に WebAuthn の登録ダイアログを表示します。
- クロスデバイスのパスキー: ハードウェアセキュリティキーを持たないユーザー向けに、プラットフォームの資格情報マネージャー(iCloud キーチェーン、Google パスワードマネージャー、1Password など)に保存された同期型パスキーをサポートします。専用のハードウェアを必要とせずにフィッシング耐性を得られます。
- アカウントリカバリー: すべてのセキュリティキーを紛失したユーザー向けのリカバリーフローを設計します:
- 登録時に生成されるリカバリーコード(印刷して保管、またはパスワードマネージャーに保存)
- 本人確認の後に管理者が監督のもとで行う再登録
- 期限付きの一時的なパスワードログインと、キーの再登録の義務化
- メールや SMS だけでのリカバリーは決して許可しないでください。これらはフィッシング耐性を損なうためです
- パスワード廃止のタイムライン: パスキーの採用率が目標のしきい値を超えた後、まず高権限アカウントでパスキーのみの認証を強制し、その後すべてのアカウントに拡大します。移行期間中は、フォールバックとしてパスワードを維持します。
- 監視と指標: 登録の成功率、認証失敗率(誤ったキー、タイムアウト、キャンセル)、認証にかかる平均時間、パスキーとパスワードによるログインの比率を追跡します。
主要な概念
| 用語 | 定義 |
|---|---|
| FIDO2 | W3C WebAuthn API と FIDO Alliance の CTAP2 プロトコルを組み合わせた総称であり、公開鍵暗号を用いてパスワードレスかつフィッシング耐性のある認証を可能にします |
| WebAuthn | W3C の Web Authentication API であり、Web アプリケーションが navigator.credentials.create()(登録)と navigator.credentials.get()(認証)を使って公開鍵クレデンシャルを作成・利用できるようにします |
| CTAP2 | Client to Authenticator Protocol バージョン 2 であり、ブラウザ(クライアント)が USB、NFC、BLE 経由で外部の認証器と通信する際に使用されるプロトコルです |
| Relying Party (RP) | 認証を要求する Web アプリケーションまたはサービスであり、RP ID(ドメイン)と RP 名(表示用の文字列)によって識別されます |
| Discoverable Credential(パスキー) | 認証器に保存されたクレデンシャルであり、RP がクレデンシャル ID を提供しなくても列挙できるため、ユーザー名を入力しない認証フローを可能にします |
| Attestation | 認証器が自身の識別情報と特性について提供する暗号学的な証明であり、RP が認証器のモデルとメーカーを検証するために使用します |
| AAGUID | Authenticator Attestation Globally Unique Identifier であり、認証器のモデルを識別する 128 ビットの値です(例:すべての YubiKey 5 NFC デバイスは同じ AAGUID を共有します) |
| Sign Count | 認証器が管理し、各アサーションに含める単調増加のカウンターであり、RP がクローンされた認証器を検出するために使用します |
| User Verification (UV) | 認証器自体でのローカル認証(PIN、指紋、顔認証)であり、認証器を所持している人物が正当な所有者であることを証明します |
ツールとシステム
- python-fido2: Yubico 公式の Python ライブラリ(v2.0 以降)であり、RP の実装用に
Fido2Server、USB 経由で認証器と直接通信するためにCtapHidDevice/Fido2Clientを提供します - YubiKey 5 Series: FIDO2/CTAP2、U2F、PIV、OpenPGP、OTP に対応する Yubico のハードウェアセキュリティキーであり、USB-A、USB-C、NFC、Nano のフォームファクターで提供されています
- py_webauthn: Duo Labs の Python WebAuthn ライブラリであり、
generate_registration_options()、verify_registration_response()、generate_authentication_options()、verify_authentication_response()の各関数を提供します - Yubico Authenticator: YubiKey の FIDO2 クレデンシャルの管理、PIN の設定、登録済みアカウントの表示を行うデスクトップ・モバイルアプリケーションです
- WebAuthn.io / demo.yubico.com: 実際の認証器に対して WebAuthn の登録・認証フローを検証するためのオンラインテストツールです
よくあるシナリオ
シナリオ: 開発チームへの FIDO2 MFA の導入
背景: あるソフトウェア企業は、50 名規模の開発チームについて、TOTP ベースの MFA をハードウェアセキュリティキーに置き換えたいと考えています。開発者は本番インフラストラクチャへの root アクセス権を持っており、フィッシング攻撃の高価値な標的となっています。同社は YubiKey 5 NFC を標準採用しています。
アプローチ:
- YubiKey 5 NFC キーを開発者 1 人あたり 2 本(メインとバックアップ)用意し、改ざん防止パッケージに入れて、初期 PIN の設定手順とともに配布する
- 社内 SSO(OAuth 2.0 / OpenID Connect)プロバイダーと統合した WebAuthn リライイングパーティーサーバーをデプロイし、
authenticator_attachment: cross-platformとuser_verification: requiredを設定する - 登録セッションを実施し、各開発者が両方のキーをアカウントに登録する。その際、アテステーション検証によって正規の YubiKey 5 NFC の AAGUID であることを確認する
- すべての開発者アカウントに対して第 2 要素として FIDO2 を必須とするよう SSO プロバイダーを設定する。その際、30 日間の猶予期間を設け、その間は TOTP も利用可能とする
- キーを自分で管理できるセルフサービスポータルを実装する。登録済みキーの確認、交換用キーの登録、紛失・盗難キーの報告(即時に認証情報を失効させ、再登録を行う)を可能にする
- 猶予期間終了後、開発者アカウントの TOTP を無効化する。認証ログを監視して、フォールバックの試行がないか確認し、残っている未対応者には 1 対 1 でサポートを提供する
- 開発チームの FIDO2 導入率 100% を達成し、本番インフラストラクチャへのアクセスにおけるフィッシングリスクをほぼゼロまで低減する
落とし穴:
- バックアップキーの登録を必須にしないと、1 本のキーを紛失した際にアカウントがロックアウトされてしまう
user_verification: discouragedを設定すると、キーを物理的に所持している者なら誰でも PIN なしで認証できてしまう- サインカウンターの検証を忘れると、クローン化されたキーによる攻撃を見逃してしまう
- タブレットやスマートフォンを主に使って作業する開発者向けに NFC をサポートしていないと、利用できなくなる
- TOTP を恒久的なフォールバックとして許可すると、FIDO2 導入のフィッシング耐性が損なわれてしまう
シナリオ: 顧客向けアプリケーションへのパスワードレスログインの実装
背景: あるEコマースプラットフォームは、クレデンシャルスタッフィングやフィッシングによるアカウント乗っ取りを減らすため、200 万人のユーザーに対して、パスワードの代替としてパスキーによるパスワードレスログインを提供したいと考えています。
アプローチ:
resident_key: requiredを指定して WebAuthn を実装し、ユーザー名なしでログインできるディスカバラブルクレデンシャルを作成するauthenticator_attachmentを省略することで、プラットフォーム認証器(Touch ID、Windows Hello、Android の生体認証)とローミング認証器(セキュリティキー)の両方をサポートする- ログインページに「パスキーでサインイン」ボタンを追加する。このボタンは、
allowCredentialsを空のリストにしてnavigator.credentials.get()を呼び出し、認証器に利用可能なパスキーを提示させる - パスキーの作成に成功したら、冗長性を確保するために第 2 のデバイスでもパスキーを作成するようユーザーに促す
- ロールアウト期間中はパスワードログインをフォールバックとして維持し、パスワードでログインするたびに、パスキーの設定を促す表示を継続的に出す
- 以下の指標を追跡する: パスキーの登録率(第 1 四半期の目標は 30%)、パスキーとパスワードのログイン比率、認証失敗率、アカウント乗っ取りのインシデント件数
- 6 か月後、パスキーのみの認証に切り替えたユーザーに対してインセンティブ(セッション期間の延長、CAPTCHA の削減など)を提供する
落とし穴:
- ユーザーのプラットフォーム認証器(例: ノートパソコンの Touch ID)が利用できず、QR コードによるクロスデバイス認証が必要になるケースを扱っていない
- すべてのユーザーが生体認証に対応したデバイスを持っていると想定してしまう。一部のユーザーは PIN ベースの検証にフォールバックする必要がある
- 登録済みのパスキーをすべて失ったユーザー向けの、適切なアカウント回復の仕組みを実装していない
- ブラウザの互換性の問題を無視してしまう。特に iOS の古いバージョンの Safari では問題が起きやすい
出力形式
## FIDO2 Deployment Report
**Application**: auth.example.com
**RP ID**: example.com
**Date**: 2026-03-19
### Enrollment Summary
- **Total Users**: 50
- **Users with Primary Key**: 50 (100%)
- **Users with Backup Key**: 47 (94%)
- **Authenticator Models**: YubiKey 5 NFC (48), YubiKey 5C NFC (2)
### Authentication Metrics (Last 30 Days)
- **Total Authentications**: 12,847
- **FIDO2 Authentications**: 12,203 (95.0%)
- **TOTP Fallback**: 644 (5.0%) -- grace period active
- **Mean Authentication Time**: 2.3 seconds
- **Authentication Failures**: 127 (0.99%)
- User cancelled: 89
- Timeout: 23
- Invalid signature: 12
- Sign count regression (possible clone): 3
### Security Events
- **Lost Key Reports**: 2
- User A: primary key lost 2026-03-12, revoked, backup promoted, new backup enrolled
- User B: backup key damaged 2026-03-15, revoked, replacement enrolled
### Credential Details
| User | Key Label | AAGUID | Registered | Last Used | Sign Count |
|------|-----------|--------|------------|-----------|------------|
| alice | YubiKey Primary | 2fc0579f... | 2026-02-15 | 2026-03-19 | 847 |
| alice | YubiKey Backup | 2fc0579f... | 2026-02-15 | 2026-03-01 | 12 |
| bob | YubiKey Primary | 2fc0579f... | 2026-02-16 | 2026-03-19 | 631 |