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

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の動作を統一(相互運用性)
データ構造attestationObjectauthDataclientDataJSON の構造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 / emailRP内部の設計自由度を保つため
エンドポイントURL/fido2-registration-challengeRESTful設計や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.iduser.namerpIdを認証器内部に保存
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仕様に基づいて作成されています。