メインコンテンツまでスキップ

FIDO2 登録フローとインターフェース詳細


概要​

このドキュメントは、W3C WebAuthn Level 2仕様のFigure 1 Registration Flowに基づいて、FIDO2登録フローの各インターフェース(①〜⑥)とパラメータを詳細に解説します。

情報源: W3C WebAuthn Level 2 - Figure 1 Registration Flow

このドキュメントで学べること:

  • Relying Party ServerとRP JavaScript間の通信(①、⑤)
  • BrowserとAuthenticator間のWebAuthn API(②、④)
  • Authenticator内部処理(③)
  • RP Serverでの検証処理(⑥)
  • 各インターフェースの標準化状況

アーキテクチャ図​

┌─────────────────────────────────────────────────────────┐
│ Relying Party Server │
│ ⑥ server │
│ validation │
└───────────────┬──────────────────▲──────────────────────┘
│ │
① challenge, ⑤ clientDataJSON,
user info, attestationObject
relying party info
│ │
┌───────────────▼──────────────────┴──────────────────────┐
│ RP JavaScript Application │
│ (Webブラウザー内で実行) │
├─────────────────────────────────────────────────────────┤
│ Browser (User Agent) │
│ WebAuthn API実装 │
└───────────────┬──────────────────▲──────────────────────┘
│ │
② relying party id, ④ new public key,
user info, credential id,
relying party info, attestation
clientDataHash
│ │
┌───────────────▼──────────────────┴──────────────────────┐
│ Authenticator │
│ ③ user verification, │
│ new keypair, │
│ attestation │
└─────────────────────────────────────────────────────────┘

W3C WebAuthn仕様の標準化範囲​

W3C WebAuthn仕様は、全てのインターフェースを標準化しているわけではありません。標準化の範囲を理解することが重要です。

✅ W3C WebAuthn仕様が標準化しているもの​

項目説明標準化の目的
JavaScript API(②、④)navigator.credentials.create() / get() のインターフェースBrowserの動作を統一(相互運用性)
データ構造attestationObject、authData、clientDataJSON の構造RP ↔ Browser ↔ Authenticator間のデータ交換を統一
検証手順(⑥)RPが実行すべき検証ステップ(Section 7.1, 7.2)セキュリティ要件の明確化
型定義PublicKeyCredentialCreationOptions 等の TypeScript/IDL 定義API仕様の明確化

標準化の範囲:

Browser(User Agent)の実装 = 完全に標準化
↓
・navigator.credentials.create() の動作
・attestationObject の生成方法
・clientDataJSON の構造
・Authenticator との通信プロトコル(CTAP)

❌ W3C WebAuthn仕様が標準化していないもの​

項目説明理由
RP Server ↔ RP JavaScript間の通信(①、⑤)HTTPエンドポイント、リクエスト/レスポンス構造各RPが独自のバックエンドAPI設計を採用できるようにするため
パラメータ名username / user_name / email 等RP内部の設計自由度を保つため
エンドポイントURL/fido2-registration-challenge 等RESTful設計やURL設計はRP次第
認証フロー全体OAuth 2.0連携、セッション管理等RPごとに認証アーキテクチャが異なるため

非標準化の範囲:

RP Server ↔ RP JavaScript の通信 = 標準化なし
↓
・HTTPエンドポイントURL
・リクエストのJSON構造
・レスポンスのJSON構造
・パラメータ名
・エラーレスポンス形式

📖 W3C仕様の明確な記述​

W3C WebAuthn Level 2 - Section 1.2 Conformance: "This specification does not define a server-side API; it only defines the client-side API."

日本語訳: "この仕様はサーバー側APIを定義していません。クライアント側APIのみを定義します。"

これの意味:

  • ✅ Browserの動作(JavaScript API、データ構造)は完全に標準化
  • ❌ RPのバックエンドAPI(①、⑤)は各実装の自由

なぜこのような設計なのか?​

観点理由
相互運用性Browserの動作を統一すれば、どのRPでも同じJavaScript APIで実装可能
柔軟性RPごとに異なるバックエンドアーキテクチャ(Node.js、Java、Python等)に対応
進化可能性RPのバックエンドは自由に進化できる(新機能追加、パフォーマンス改善等)
責任分離W3CはBrowser実装を標準化し、RPはセキュリティ要件(検証手順)のみ遵守

実例: idp-server、Google、GitHub、Microsoftは全て異なるバックエンドAPI設計ですが、全て同じWebAuthn APIで動作します。


① RP Server → RP JavaScript: チャレンジ取得​

通信: HTTP(各実装が自由に設計)

一般的なリクエスト​

POST /fido2-registration-challenge
Content-Type: application/json

{
"username": "user@example.com"
}

一般的なレスポンス​

{
"challenge": "Y2hhbGxlbmdl...",
"rp": {
"id": "example.com",
"name": "Example Service"
},
"user": {
"id": "dXNlcjEyMw",
"name": "user@example.com",
"displayName": "User Name"
},
"pubKeyCredParams": [
{"type": "public-key", "alg": -7},
{"type": "public-key", "alg": -257}
],
"timeout": 60000,
"authenticatorSelection": {
"authenticatorAttachment": "platform",
"residentKey": "required",
"userVerification": "required"
},
"attestation": "none"
}

主要パラメータ​

パラメータ型説明例
challengeBase64URLランダムチャレンジ(32バイト以上推奨)"Y2hhbGxlbmdl..."
rp.idStringRPのドメイン名(フィッシング対策の要)"example.com"
rp.nameStringRPの表示名"Example Service"
user.idBase64URLユーザーID(バイナリ、ユーザー識別の要)"dXNlcjEyMw"
user.nameStringユーザー識別子"user@example.com"
user.displayNameStringユーザー表示名"User Name"
pubKeyCredParamsArray許可する署名アルゴリズムES256(-7), RS256(-257)
timeoutNumberタイムアウト(ミリ秒)60000
authenticatorSelection.authenticatorAttachmentString認証器タイプ制約"platform" / "cross-platform" / null
authenticatorSelection.residentKeyStringResident Key要件"required" / "preferred" / "discouraged"
authenticatorSelection.userVerificationStringUser Verification要件"required" / "preferred" / "discouraged"
attestationStringAttestation要件"none" / "indirect" / "direct"

セキュリティ要件​

  • ✅ challengeは暗号学的に安全なランダム値(32バイト以上推奨)
  • ✅ サーバー側でチャレンジを一時保存(検証時に使用、1回のみ有効)
  • ✅ user.idはユーザーごとに一意(個人識別情報を含まない推奨)
  • ✅ チャレンジの有効期限を設定(例: 2分)

② Browser → Authenticator: WebAuthn API呼び出し​

通信: WebAuthn API(W3C標準)

JavaScriptコード​

// ① で取得したレスポンスを変換
const publicKeyOptions = {
challenge: base64UrlToBuffer(serverResponse.challenge),
rp: serverResponse.rp,
user: {
id: base64UrlToBuffer(serverResponse.user.id),
name: serverResponse.user.name,
displayName: serverResponse.user.displayName
},
pubKeyCredParams: serverResponse.pubKeyCredParams,
timeout: serverResponse.timeout,
authenticatorSelection: serverResponse.authenticatorSelection,
attestation: serverResponse.attestation
};

// WebAuthn API呼び出し
const credential = await navigator.credentials.create({
publicKey: publicKeyOptions
});

Browserから認証器へ渡されるデータ​

データ説明由来
rp.idRPのドメイン名サーバーから受領
rp.nameRPの表示名サーバーから受領
user.idユーザーID(バイナリ)サーバーから受領
user.nameユーザー識別子サーバーから受領
user.displayNameユーザー表示名サーバーから受領
clientDataHashclientDataJSONのSHA-256ハッシュBrowser内部で生成
authenticatorSelection認証器選択基準サーバーから受領

clientDataJSONの内容​

{
"type": "webauthn.create",
"challenge": "Y2hhbGxlbmdl...",
"origin": "https://example.com",
"crossOrigin": false
}

重要: BrowserはclientDataJSONを自動生成し、そのSHA-256ハッシュを認証器に渡します。開発者はclientDataJSONを直接構築する必要はありません。


③ Authenticator内部: 鍵ペア生成とAttestation作成​

認証器の処理 (FIDO CTAP仕様準拠):

1. ユーザー検証(User Verification)​

設定値動作
userVerification="required"生体認証またはPIN入力を必須とする
userVerification="preferred"可能なら検証、不可能ならスキップ
userVerification="discouraged"検証なし(タップのみ)

2. 鍵ペア生成​

  • pubKeyCredParamsで指定されたアルゴリズムで鍵ペア生成(例: ES256)
  • 秘密鍵は認証器内部のSecure Elementに安全に保管
  • 公開鍵はRP Serverに送信

3. Credential ID生成​

  • ランダムな一意識別子を生成
  • 認証時にCredentialを特定するために使用

4. Resident Key(Discoverable Credential)処理​

設定値動作
residentKey="required"user.id、user.name、rpIdを認証器内部に保存
residentKey="preferred"可能なら保存、不可能ならスキップ
residentKey="discouraged"保存しない

用途: residentKey="required"の場合、パスワードレスログイン(ユーザー名入力不要)が可能になります。

5. Attestation Statement作成​

設定値動作
attestation="none"Attestation Statementを省略(最も一般的)
attestation="indirect"匿名化されたAttestation
attestation="direct"認証器の証明書チェーン付きAttestation

④ Authenticator → Browser: Attestation Response返却​

認証器がBrowserに返すデータ:

// credential.response の内容
{
attestationObject: ArrayBuffer, // CBOR形式のバイナリ
clientDataJSON: ArrayBuffer // JSON文字列のバイナリ
}

attestationObject の内容(CBOR形式)​

{
"fmt": "none",
"authData": <バイナリデータ>,
"attStmt": {}
}

authData の構造(37バイト以上)​

フィールドサイズ説明
rpIdHash32バイトrpIdのSHA-256ハッシュ(フィッシング検出に使用)
flags1バイトUP, UV, AT, BE, BS等のフラグ
signCount4バイト署名カウンター(クローン検出に使用)
attestedCredentialData可変長aaguid + credentialIdLength + credentialId + credentialPublicKey

flags(1バイト)の内訳​

ビット名称説明
bit 0UP (User Present)ユーザーがタップした(物理的存在確認)
bit 2UV (User Verified)生体認証またはPIN入力が完了
bit 6AT (Attested Credential Data)attestedCredentialDataが含まれる(登録時は常に1)
bit 3BE (Backup Eligibility)バックアップ可能(Level 3で追加)
bit 4BS (Backup State)バックアップ済み(Level 3で追加)

attestedCredentialData の構造​

aaguid (16 bytes)
↓ 認証器モデル識別子

credentialIdLength (2 bytes)
↓

credentialId (credentialIdLength bytes)
↓ Credential一意識別子

credentialPublicKey (COSE形式、可変長)
↓ 公開鍵(ES256の場合は約77バイト)

主要パラメータ​

パラメータ説明用途
credentialIdCredential一意識別子認証時の指定に使用
credentialPublicKey公開鍵(COSE形式)署名検証に使用
flags.UPUser Present(タップ確認)ユーザーの物理的存在確認
flags.UVUser Verified(生体認証/PIN)ユーザー本人確認
flags.ATAttested Credential Data存在フラグ登録時は常に1
aaguid認証器モデル識別子認証器の種類識別

⑤ RP JavaScript → RP Server: Attestation送信​

通信: HTTP(各実装が自由に設計)

一般的なリクエスト​

POST /fido2-registration
Content-Type: application/json

{
"id": "credential_id_base64url",
"rawId": "credential_id_base64url",
"type": "public-key",
"response": {
"clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIi...",
"attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVi..."
}
}

JavaScriptコード例​

// ArrayBufferをBase64URLに変換
function bufferToBase64Url(buffer) {
const bytes = new Uint8Array(buffer);
let binary = '';
for (let i = 0; i < bytes.length; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}

// Credential送信
const response = await fetch('/fido2-registration', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
id: credential.id,
rawId: bufferToBase64Url(credential.rawId),
type: credential.type,
response: {
clientDataJSON: bufferToBase64Url(credential.response.clientDataJSON),
attestationObject: bufferToBase64Url(credential.response.attestationObject)
}
})
});

主要パラメータ​

パラメータ型説明
idStringCredential ID(Base64URL)
rawIdStringCredential ID(Base64URL、idと同じ)
typeString常に "public-key"
response.clientDataJSONStringclientDataJSONのBase64URL
response.attestationObjectStringattestationObjectのBase64URL

⑥ RP Server内部: サーバー側検証​

検証項目(W3C仕様 Section 7.1準拠):

1. clientDataJSON検証​

✅ type が "webauthn.create" であること
✅ challenge が保存済みチャレンジと一致すること
✅ origin が許可リストに含まれること
✅ crossOrigin が false であること(Same Origin検証)

2. attestationObject検証​

✅ authData.rpIdHash が rpId のSHA-256ハッシュと一致すること
✅ authData.flags.UP が 1(User Present)であること
✅ authData.flags.UV が要求通り(userVerification="required"の場合)
✅ authData.flags.AT が 1(Attested Credential Data存在)

rpIdHash検証の重要性:

  • フィッシング攻撃防止の要
  • 攻撃者が別ドメインで登録した鍵をRPに送信しても、rpIdHashが一致しないため検証失敗

3. 公開鍵抽出と保存​

✅ attestedCredentialData から credentialId と credentialPublicKey を抽出
✅ データベースに保存(user_id, credential_id, public_key, sign_count等)

4. Attestation Statement検証(attestation != "none"の場合)​

✅ 認証器の証明書チェーン検証
✅ FIDO Metadata Serviceとの照合
✅ 信頼できる認証器かどうかの判定

注意: attestation="none"が最も一般的で、多くのRPはAttestation検証を省略しています。

レスポンス例​

HTTP/1.1 200 OK
Content-Type: application/json

{
"status": "success",
"credential_id": "credential_id_base64url"
}

インターフェース標準化状況まとめ​

IF通信標準化状況備考
① RP Server → RP JavaScriptHTTP❌ 標準化なし各RP実装が自由に設計
② Browser → AuthenticatorWebAuthn API✅ W3C標準navigator.credentials.create()
③ Authenticator内部処理-✅ FIDO CTAP仕様鍵ペア生成、User Verification
④ Authenticator → BrowserWebAuthn API✅ W3C標準AuthenticatorAttestationResponse
⑤ RP JavaScript → RP ServerHTTP❌ 標準化なし各RP実装が自由に設計
⑥ RP Server内部検証-✅ W3C標準(検証手順)Section 7.1で手順規定

重要な結論:

  • W3C WebAuthn仕様は、②、④のクライアント側APIと⑥の検証手順のみ標準化
  • ①、⑤のRP ServerとRP JavaScript間の通信は標準化されていない
  • 各RPが独自のAPI設計(エンドポイント、パラメータ名、データ構造)を採用可能
  • idp-server、Google、GitHub等、各サービスでAPI設計が異なる

まとめ​

重要なポイント​

  1. 標準化の範囲

    • ✅ クライアント側API(②、④): W3C WebAuthn標準
    • ✅ 認証器処理(③): FIDO CTAP標準
    • ✅ 検証手順(⑥): W3C WebAuthn標準(Section 7.1)
    • ❌ RP ServerとRP JavaScript間の通信(①、⑤): 標準化なし
  2. セキュリティの要

    • challenge: 暗号学的に安全なランダム値(32バイト以上)
    • rpIdHash: フィッシング攻撃防止
    • origin: Same Origin検証
    • flags.UP: ユーザーの物理的存在確認
  3. データの流れ

    • サーバー → ブラウザー: challenge, rp, user, pubKeyCredParams
    • ブラウザー → 認証器: rp, user, clientDataHash
    • 認証器 → ブラウザー: attestationObject, clientDataJSON
    • ブラウザー → サーバー: id, type, response
  4. 実装の自由度

    • ①、⑤のインターフェースはRP実装ごとに異なる
    • エンドポイント名、パラメータ名、HTTPメソッド等は自由
    • WebAuthn4j等のライブラリで検証を簡素化可能

参考リソース​

W3C WebAuthn Level 2仕様​

FIDO仕様​

関連ドキュメント​


このドキュメントは、W3C WebAuthn Level 2仕様に基づいて作成されています。