22.3K Views
November 26, 18
スライド概要
HTML5 Conference2018で発表した内容です。 #html5j #html5j_a
2023年10月からSpeaker Deckに移行しました。最新情報はこちらをご覧ください。 https://speakerdeck.com/lycorptech_jp
FIDO認証による パスワードレスログイン実装入門 HTML5 Conference 2018 ヤフー株式会社 合路 健人 @kg0r0 2018/11/25 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved.
アジェンダ 1. 自己紹介 2. パスワード認証の課題 3. FIDOとは 4. FIDOの技術仕様 5. デモ 6. Web Authenticationフロー 7. まとめ Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 2
自己紹介 合路 健人 新卒入社2年目 システム統括本部 プラットフォーム開発本部 アイデンティティー&アクセスマネジメント部 社内における認証をよりセキュアで使いやすくする取り組み 興味のある方は是非一緒にどうですか?? Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved.
パスワード認証の課題 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 4
パスワード認証の課題(利便性) こんなことはありませんか 認証機会の増加 サービスごとに異なるパスワードを登録 複雑なパスワードの入力操作が不便 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 5
パスワード認証の課題(安全性) username + password Client Server DB Password List Attack Dictionary Attack Phishing Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. DB Leak 6
FIDOとは Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 7
FIDO(Fast IDentity Online) Strong Weak SECURITY パスワードへの依存度を減らし、利便性と安全性を向上する SECURITY USABILITY Poor Easy USABILITY https://www.slideshare.net/FIDOAlliance/introduction-to-fido-alliance-66730790 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. https://www.slideshare.net/FIDOAlliance/fido-83445442 8
FIDO認証モデル 公開鍵暗号方式による認証 オンラインサービスへの登録時に鍵ペアを生成して登録 challenge User verification client Authenticator gesture FIDO Authentication signature RP publickey privatekey Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 9
用語説明 RP(Relying Party) • ユーザーの登録、認証を⾏うウェブサイト、または、事業者 認証器(Authenticator) • キーペアの管理を行う • 外部認証器と内部認証器がある Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 10
利便性 認証行為が容易 複数のパスワードを覚える負担を軽減 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 11
認証行為が容易 生体認証の導入 • 指紋・虹彩・顔・声紋など 認証器を指定・選択できるポリシー • 認証器の多様性 • 認証器の選択 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 12
複数のパスワードを覚える負担を軽減 パスワード(記憶) • 本人のみが記憶している情報 認証器(所持) • 本人のみが所持している物 • 認証器が利用者本人を検証する機能を持つ Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 13
安全性 クレデンシャル情報の通信経路 クレデンシャル情報の安全性 認証器の信頼性 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 14
クレデンシャル情報の通信経路 パスワード認証 Client User リモート認証 (ユーザー名, パスワード) FIDO認証モデル User Relying party Authenticator ローカル認証 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. (指紋など) client リモート認証 (署名) Relying party 15
クレデンシャル情報の安全性 RPごとに鍵ペアを生成して登録 リスト型攻撃に対する耐性 フィッシングに対する耐性 rpid1(origin) FIDO Authentication Authenticator Relying party1 Kpub1 rpid2(origin) User Presence Kpriv1 Kpriv2 Client Kpub2 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. Relying party2 16
クレデンシャル情報の安全性(リスト型攻撃対策) RPごとに異なる公開鍵を登録 rpid1(origin) FIDO Authentication Authenticator Kpub1 Relying party1 rpid2(origin) User Presence Kpriv1 Kpriv2 Client Kpub2 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. Relying party2 17
クレデンシャル情報の安全性(フィッシング対策) RPごとに異なる秘密鍵を保管 Authenticator FIDO Authentication Relying party1 Kpub1 Relying party2 Kpub2 Kpriv1 User Presence Kpriv2 Client Relying party3 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 18
認証器の信頼性 Attestation & Metadata Authenticator Attestation privKey Attestation Certificate Signed Attestation Object Metadata Registration Attestation pubKey Relying party Authentication Keys Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 19
FIDOの技術仕様 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 20
FIDOの3つの技術仕様 FIDO UAF(Universal Authentication Framework) FIDO U2F(Universal 2nd Factor) FIDO2 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 21
FIDO UAF パスワードレス型(所持+生体など) 主にスマートフォン端末での利用を想定 生体認証などの認証手段によりパスワードレスを実現 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. https://www.slideshare.net/FIDOAlliance/fido-83445442 22
FIDO U2F パスワード補完型(記憶+所持) 主にPC上でWebブラウザの利用を想定した二要素認証 着脱方式と無線方式に対応 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. https://www.slideshare.net/FIDOAlliance/fido-83445442 23
UAFとU2F ユースケースが異なる • UAF(FIDOクライアント=アプリ) • U2F(ブラウザから利用できるが二要素認証) ブラウザからパスワードレス認証を!! Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. https://www.slideshare.net/FIDOAlliance/fido-83445442 24
FIDO2 FIDO認証対応プラットフォームの拡大 Web Authentication API ブラウザからクレデンシャルにアクセスするAPI CTAP クライアントと外部認証器間の通信をサポートするプロトコル Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. https://www.slideshare.net/FIDOAlliance/fido-psd2-solving-the-strong-customer-authentication-challenge-in-europe 25
Web Authentication API ブラウザからクレデンシャルにアクセスするAPI ブラウザの標準インターフェース Credential Management API の拡張 登録 : navigator.credentials.create() 認証 : navigator.credentials.get() Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 26
CTAP クライアントと外部認証器間の通信をサポートするプロトコル 物理的に異なるクライアントと外部認証器が連携 USB・BLE・NFC などをサポート USB, BLE, NFC, Authenticator Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. client 27
様々な認証器に対応 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. https://www.slideshare.net/FIDOAlliance/fido-83445442 28
デモ Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 29
Web Authentication(登録) Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 30
登録フロー(概略) Authenticator Relying party Client ユーザー名 チャレンジなど チャレンジ・RPの情報・ユーザーの情報 鍵ペアの生成 Attestation response (署名・証明書・公開鍵など) Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. Attestation response 検証 (署名・証明書・公開鍵など) 公開鍵の保存 31
実装範囲 Authenticator Relying party Client Web Authentication API 実装が必要な箇所 ユーザー名 チャレンジなど チャレンジ・RPの情報・ユーザーの情報 鍵ペアの生成 Attestation response (署名・証明書・公開鍵など) Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. Attestation response 検証 (署名・証明書・公開鍵など) 公開鍵の保存 32
登録フロー(概略) Authenticator Relying party Client ユーザー名 チャレンジなど チャレンジ・RPの情報・ユーザーの情報 鍵ペアの生成 Attestation response (署名・証明書・公開鍵など) Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. Attestation response 検証 (署名・証明書・公開鍵など) 公開鍵の保存 33
ユーザー名の入力 フォームからユーザー名を取得 ユーザー名を送信してチャレンジなどの情報を要求 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 34
ユーザー名の送信
フォームからユーザー名を取得
ユーザー名を送信してチャレンジなどの情報を要求
fetch('/webauthn/register', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: this.username.value,
name: this.name.value
})
})
Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved.
サンプル
35
challengeの送信
{
}
// random number
challenge: randomBase64URLBuffer(32),
rp: {
name: "FIDO Examples Corporation”
},
user: {
id: id,
name: username,
displayName: displayName
},
pubKeyCredParams: [
{
type: "public-key",
alg: -7 // "ES256" IANA COSE Algorithms registry
}
]
Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved.
サンプル
36
登録フロー(概略) Authenticator Relying party Client Web Authentication API ユーザー名 チャレンジなど チャレンジ・RPの情報・ユーザーの情報 鍵ペアの生成 Attestation response (署名・証明書・公開鍵など) Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. Attestation response 検証 (署名・証明書・公開鍵など) 公開鍵の保存 37
navigator.credentials.create()
navigator.credentials.create({
publicKey: {
rp: {
id: "example.com”,
name: "Acme"
},
user: {
id: new Uint8Array(16),
name: "[email protected]",
displayName: "John P. Smith "
},
pubKeyCredParams: [{
type: "public-key",
alg: -7
}],
attestation: "direct",
timeout: 60000,
// must be a cryptographically random number sent from a server
challenge: new Uint8Array([
0x8C, 0x0A, 0x26, 0xFF, 0x22, 0x91, 0xC1, 0xE9, ... ]).buffer
}
})
Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved.
サンプル
38
Parameter rp [Object] (必須) • Relying partyに関する情報 user [Object](必須) • Credentialが紐づくユーザー情報 challenge[ArrayBuffer] (必須) • サーバーで生成した乱数 pubKeyCredParams[Object] (必須) • Credentialのタイプとアルゴリズムの指定(typeはpublic-key固定) timeout [Int] • ユーザーの入力待機時間 attestation [String] • Authenticatorの出どころを検証するかどうか authenticatorSelection [Object] • 認証器への要求事項 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 39
navigator.credentials.create() Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 40
登録フロー(概略) Authenticator Relying party Client ユーザー名 チャレンジなど チャレンジ・RPの情報・ユーザーの情報 鍵ペアの生成 Attestation response (署名・証明書・公開鍵など) Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. Attestation response 検証 (署名・証明書・公開鍵など) 公開鍵の保存 41
AuthenticatorAttestationResponse
{
// credential identifier on the device
"rawId": "imCIoe8U_N9M1rTGeCqJ96TAu5uqSPa7YUzd ... ",
"id": ” imCIoe8U_N9M1rTGeCqJ96TAu5uqSPa7YUzdh7qq ... ",
// attestation data
"response": {
"clientDataJSON": ”eyJjaGFsbGVuZ2UiOiJJSFdtWjFPa1MydDZLaH ... ”,
"attestationObject": ”o2NmbXRoZmlkby11MmZnYXR0U3RtdKJjc2 ... "
},
// extensions results struct
"getClientExtensionResults": {},
}
// type of credential
"type": "public-key”
Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved.
サンプル
42
clientDataJSON
{
// random number
"challenge": "IHWmZ1OkS2t6KhvX-koNxutkYuMVEunCjYNSXXgAxvU",
// origin of the website
"origin": "http://localhost:3000",
}
// type of the call.
"type": "webauthn.create”
サンプル
Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved.
43
attestationObject 公開鍵などを含むCBORでエンコードされたデータ CBOR(Concise Binary Object Representation) • JSONフォーマットをバイナリで表現 • JSONよりもサイズが小さい const cbor = require('cbor'); ... let attestationBuffer = base64url.toBuffer(webAuthnResponse.response.attestationObject); let ctapMakeCredResp = cbor.decodeAllSync(attestationBuffer)[0]; サンプル Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 44
attestationObject 公開鍵などを含むCBORでエンコードされたデータ { // attestation format "fmt": ”packed", // raw buffer struct containing user info "authData": "9569088f1ecee3232954035dbd10d7cae3 ... ", } // attestation statement data "attStmt": { // signature "sig": "3046022100db3162cfa7b5dbd78c46864e5f9 ... ", // X.509 Certificate Chain "x5c": [ "3082013c3081e4a003020102020a395187893878526 ... ” ] } Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. サンプル 45
authData ユーザーの情報を含むバイナリデータ Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. https://medium.com/@herrjemand/verifying-fido2-responses-4691288c8770 46
authDataのパース let parseMakeCredAuthData = (buffer) => { let rpIdHash = buffer.slice(0, 32); buffer = buffer.slice(32); let flagsBuf = buffer.slice(0, 1); buffer = buffer.slice(1); let flags = flagsBuf[0]; let counterBuf = buffer.slice(0, 4); buffer = buffer.slice(4); let counter = counterBuf.readUInt32BE(0); let aaguid = buffer.slice(0, 16); buffer = buffer.slice(16); let credIDLenBuf = buffer.slice(0, 2); buffer = buffer.slice(2); let credIDLen = credIDLenBuf.readUInt16BE(0); let credID = buffer.slice(0, credIDLen); buffer = buffer.slice(credIDLen); let COSEPublicKey = buffer; return {rpIdHash, flagsBuf, flags, counter, counterBuf, aaguid, credID, credIDLen, credIDLenBuf, COSEPublicKey} } サンプル Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 47
authDataのパース結果
{
// hash of the rpId
rpIdHash: <Buffer 49 96 0d e5 88 0e 8c 68 74 34 17 0f 64 76 60 5b 8f ... >,
// state of the authenticator during the authentication
flags: 65,
flagsBuf: <Buffer 41>,
// 4byte counter
counter: 53,
counterBuf: <Buffer 00 00 00 35>,
// authenticator attestation identifier
aaguid: <Buffer f8 a0 11 f3 8c 0a 4d 15 80 06 17 11 1f 9e dc 7d>,
// credential Identifier
credID: <Buffer de 13 93 92 33 87 5a 0e 89 1b de 60 dc 43 1f 89 8c ... >,
credIDLenBuf: <Buffer 00 40>,
credIDLen: 64,
}
// COSE encoded public key
COSEPublicKey: <Buffer a5 01 02 03 26 20 01 21 58 20 36 25 71 4a ... >
Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved.
サンプル
48
登録フロー(概略) Authenticator Relying party Client ユーザー名 チャレンジなど チャレンジ・RPの情報・ユーザーの情報 鍵ペアの生成 Attestation response (署名・証明書・公開鍵など) Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. Attestation response 検証 (署名・証明書・公開鍵など) 公開鍵の保存 49
Verify Attestation challenge、origin、typeの検証 flagsの検証 signatureの検証 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 50
challengeの検証
clientDataJSON内のchallengeを検証
router.post('/response', (request, response) => {
let webauthnResp = request.body
let clientData =
JSON.parse(base64url.decode(webauthnResp.response.clientDataJSON));
if(clientData.challenge !== request.session.challenge) {
response.json({
'status': 'failed',
'message': 'Challenges don¥'t match!’
})
}
...
サンプル
})
Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved.
51
originの検証
clientDataJSON内のoriginを検証
router.post('/response', (request, response) => {
let webauthnResp = request.body
let clientData =
JSON.parse(base64url.decode(webauthnResp.response.clientDataJSON));
...
if(clientData.origin !== config.origin) {
response.json({
'status': 'failed',
'message': 'Origins don¥'t match!’
})
}
サンプル
})
Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved.
52
typeの検証 clientDataJSON内のtypeがwebauthn.createであるか確認 ... let webauthnResp = request.body let clientData = JSON.parse(base64url.decode(webauthnResp.response.clientDataJSON)); if(clientData.type !== 'webauthn.create') { ... } }) サンプル Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 53
Verify Attestation challenge、origin、typeの検証 flagsの検証 signatureの検証 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 54
flagsの検証 AuthenticatorでのUP、UVの結果 UP : User Presence • ユーザーの存在確認(必須) UV : User Verification • ユーザーの認証(任意) Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. https://slides.com/fidoalliance/jan-2018-fido-seminar-webauthn-tutorial#/ 55
flagsの検証 AuthenticatorでのUP、UVの結果 UP : User Presence • ユーザーの存在確認(必須) UV : User Verification • ユーザーの認証(任意) let authrDataStruct = parseMakeCredAuthData(ctapMakeCredResp.authData); if(!(authrDataStruct.flags & 0x01)) throw new Error('User was NOT presented durring authentication!'); サンプル Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 56
Verify Attestation challenge、origin、typeの検証 flagsの検証 signatureの検証 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 57
signatureの検証 Attestation Certificateの取得 authDataのパラメーターとclientDataJSONのハッシュ値を連結 attStmtからsignatureを取得 signatureを検証 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 58
Attestation Certificateの取得
authData内にあるattSmtのx5c(X.509 Certificate Chain)
PEM形式に変換する
• Base64エンコード
• 64文字ごとに改行コードをいれる
• ヘッダとフッタを入れる
let ASN1toPEM = (pkBuffer) => {
let type;
}
...
type = 'CERTIFICATE';
}
let b64cert = pkBuffer.toString('base64');
let PEMKey = '';
for(let i = 0; i < Math.ceil(b64cert.length / 64); i++) {
let start = 64 * i;
PEMKey += b64cert.substr(start, 64) + '¥n';
}
PEMKey = `-----BEGIN ${type}-----¥n` + PEMKey + `-----END ${type}-----¥n`;
return PEMKey
Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved.
サンプル
59
signatureの検証 ... let let let let attestationBuffer = base64url.toBuffer(attestationObject); ctapMakeCredResp = cbor.decodeAllSync(attestationBuffer)[0]; authrDataStruct = parseMakeCredAuthData(ctapMakeCredResp.authData); clientDataHash = hash(base64url.toBuffer(clientDataJSON)) // Attestation Certificateの取得 let PEMCertificate = ASN1toPEM(ctapMakeCredResp.attStmt.x5c[0]); // authDataのパラメーターとclientDataJSONのハッシュ値を連結 let signatureBase = Buffer.concat([ authrDataStruct.rpIdHash, authrDataStruct.flagsBuf, authrDataStruct.counterBuf, authrDataStruct.aaguid, authrDataStruct.credIDLenBuf, authrDataStruct.credID, authrDataStruct.COSEPublicKey, clientDataHash ]); // attStmtからsignatureの取得 let signature = ctapMakeCredResp.attStmt.sig; } // 検証 response.verified = crypto .createVerify('SHA256') .update(signatureBase) .verify(PEMCertificate, signature); Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. サンプル 60
Verify Attestation challenge、origin、typeの検証 flagsの検証 signatureの検証 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 61
登録成功!! Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 62
登録フロー(概略) Authenticator Relying party Client ユーザー名 チャレンジなど チャレンジ・RPの情報・ユーザーの情報 鍵ペアの生成 Attestation response (署名・証明書・公開鍵など) Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. Attestation response 検証 (署名・証明書・公開鍵など) 公開鍵の保存 63
publicKeyの保存
COSE(CBOR Object Signing and Encryption)
•
暗号鍵を表現するためのJSONをCBORエンコードしたフォーマット
• COSEから取得したxとyを連結し、先頭に0x04を加えるとPublicKeyになる
let verifyAuthenticatorAttestationResponse = (webAuthnResponse) => {
...
let authrDataStruct = parseMakeCredAuthData(ctapMakeCredResp.authData);
// COSE PublicKeyをPKCS形式に変換
let publicKey
= COSEECDHAtoPKCS(authrDataStruct.COSEPublicKey)
let verified = verifySignature(signature, signatureBase, PEMCertificate)
if(verified) {
// publicKeyなどを保存
response.authrInfo = {
fmt: ’packed',
publicKey: base64url.encode(publicKey),
counter: authrDataStruct.counter,
credID: base64url.encode(authrDataStruct.credID)
}
}
}
}
return response
Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved.
サンプル
64
publicKeyの保存 COSE(CBOR Object Signing and Encryption) • 暗号鍵を表現するためのJSONをCBORエンコードしたフォーマット • COSEから取得したxとyを連結し、先頭に0x04を加えるとPublicKeyになる // result of COSE parse { 1: kty=2, 3: alg=-7, -1: crv=1, -2: x, -3: y, } Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. サンプル 65
publicKeyの保存
COSE(CBOR Object Signing and Encryption)
•
暗号鍵を表現するためのJSONをCBORエンコードしたフォーマット
• COSEから取得したxとyを連結し、先頭に0x04を加えるとPublicKeyになる
const cbor
...
= require('cbor');
let COSEECDHAtoPKCS = (COSEPublicKey) => {
let coseStruct = cbor.decodeAllSync(COSEPublicKey)[0];
let tag = Buffer.from([0x04]);
let x = coseStruct.get(-2);
let y = coseStruct.get(-3);
return Buffer.concat([tag, x, y])
}
Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved.
サンプル
66
Web Authentication(認証) Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 67
認証フロー(概略) Authenticator Relying party Client ユーザー名 チャレンジやユーザーの情報など チャレンジ・RPの情報・ユーザーの情報 署名の生成 Assertion response (署名など) Assertion response (署名など) Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 検証 68
実装範囲 Authenticator Relying party Client Web Authentication API 実装が必要な箇所 ユーザー名 チャレンジやユーザーの情報など チャレンジ・RPの情報・ユーザーの情報 署名の生成 Assertion response (署名など) Assertion response (署名など) Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 検証 69
認証フロー(概略) Authenticator Relying party Client ユーザー名 チャレンジやユーザーの情報など チャレンジ・RPの情報・ユーザーの情報 署名の生成 Assertion response (署名など) Assertion response (署名など) Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 検証 70
ユーザー名の入力 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 71
ユーザー名の送信
fetch('/webauthn/login', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: this.username.value
})
})
サンプル
Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved.
72
challengeの送信
let generateServerGetAssertion = (authenticators) => {
let allowCredentials = [];
for(let authr of authenticators) {
allowCredentials.push({
type: 'public-key',
id: authr.credID,
transports: ['usb', 'nfc', 'ble']
})
}
return {
// random number
challenge: randomBase64URLBuffer(32),
allowCredentials: allowCredentials
}
}
Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved.
サンプル
73
実装範囲 Authenticator Relying party Client Web Authentication API ユーザー名 チャレンジやユーザーの情報など チャレンジ・RPの情報・ユーザーの情報 署名の生成 Assertion response (署名など) Assertion response (署名など) Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 検証 74
navigator.credentials.get()
navigator.credentials.get({
publicKey: {
timeout: 60000,
challenge: new Uint8Array([
0x79, 0x50, 0x68, 0x71, 0xDA, 0xEE, 0xEE, 0xB9, 0x94, 0xC3, 0xC2, ...
]).buffer,
allowCredentials: [{
id: cred.rawId,
transports: ["usb", "nfc", "ble"],
type: "public-key"
}]
},
})
サンプル
Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved.
75
Parameter challenge [ArrayBuffer] (必須) • サーバーで生成した乱数 timeout [Int] • ユーザーの入力待機時間 allowCredentials [Array] • ユーザーに紐づくCredentialのidリスト rpId [String] • rpIdの指定(登録時と同じ値を指定) userVerification [String] • ユーザーに紐づくCredentialのリスト Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 76
navigator.credentials.get() Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 77
実装範囲 Authenticator Relying party Client ユーザー名 チャレンジやユーザーの情報など チャレンジ・RPの情報・ユーザーの情報 署名の生成 Assertion response (署名など) Assertion response (署名など) Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 検証 78
AuthenticatorAssertionResponse
{
// credential identifier on the device
"rawId": "imCIoe8U_N9M1rTGeCqJ96TAu5uqSPa7YUzd ... " ,
"id": "imCIoe8U_N9M1rTGeCqJ96TAu5uqSPa7YUzdh7qq ... ",
// assertion data
"response": {
"authenticatorData": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MBAAAALQ",
"signature": "MEUCIQCFOqnsAFZLQmcPt2qSjnCb403SisGEASSjT3fOPuD5JgIgF ...",
"userHandle": "",
"clientDataJSON": "eyJjaGFsbGVuZ2UiOiJDUXF5aUlrQ00yWEtvaHVSdlNqTEFoN ...”
},
// extensions results struct
"getClientExtensionResults": {},
}
// type of credential
"type": "public-key”
Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved.
サンプル
79
authenticatorData ユーザーの情報を含むバイナリデータ https://slides.com/fidoalliance/jan-2018-fido-seminar-webauthn-tutorial#/ Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 80
authenticatorDataのパース let parseGetAssertAuthData = (buffer) => { let rpIdHash = buffer.slice(0, 32); buffer = buffer.slice(32); let flagsBuf = buffer.slice(0, 1); buffer = buffer.slice(1); let flags = flagsBuf[0]; let counterBuf = buffer.slice(0, 4); buffer = buffer.slice(4); let counter = counterBuf.readUInt32BE(0); return {rpIdHash, flagsBuf, flags, counter, counterBuf} } サンプル Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 81
authenticatorDataのパース結果
{
// hash of the rpId
rpIdHash: <Buffer 49 96 0d e5 88 0e 8c 68 74 34 17 0f 64 76 60 5b 8f e4 ae b9
a2 86 32 c7 99 5c f3 ba 83 1d 97 63>,
// state of the authenticator during the authentication
flags: 1,
flagsBuf: <Buffer 01>,
}
// 4byte counter
counter: 68,
counterBuf: <Buffer 00 00 00 44>
サンプル
Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved.
82
認証フロー(概略) Authenticator Relying party Client ユーザー名 チャレンジやユーザーの情報など チャレンジ・RPの情報・ユーザーの情報 署名の生成 Assertion response (署名など) Assertion response (署名など) Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 検証 83
Verify Assertion challenge、origin、typeの検証 flagsの検証 signatureの検証 counterの検証と更新 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 84
challengeの検証
clientDataJSONとセッションなどに保存していたchallengeを比較
router.post('/response', (request, response) => {
let webauthnResp = request.body
let clientData =
JSON.parse(base64url.decode(webauthnResp.response.clientDataJSON));
if(clientData.challenge !== request.session.challenge) {
response.json({
'status': 'failed',
'message': 'Challenges don¥'t match!’
})
}
...
サンプル
})
Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved.
85
originの検証
clientDataJSONとサーバーのコンフィグなどから取得したoriginを比較
router.post('/response', (request, response) => {
let webauthnResp = request.body
let clientData =
JSON.parse(base64url.decode(webauthnResp.response.clientDataJSON));
...
if(clientData.origin !== config.origin) {
response.json({
'status': 'failed',
'message': 'Origins don¥'t match!’
})
}
サンプル
})
Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved.
86
typeの検証 clientDataJSONのtypeがwebauthn.getであるか確認 ... let webauthnResp = request.body let clientData = JSON.parse(base64url.decode(webauthnResp.response.clientDataJSON)); if(clientData.type !== 'webauthn.get') { ... } }) サンプル Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 87
Verify Assertion challenge、origin、typeの検証 flagsの検証 signatureの検証 counterの検証と更新 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 88
flagsの検証 AuthenticatorでのUP、UVの結果を検証 let authrDataStruct = parseMakeCredAuthData(ctapMakeCredResp.authData); if(!(authrDataStruct.flags & 0x01)) throw new Error('User was NOT presented durring authentication!'); サンプル Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 89
Verify Assertion challenge、origin、typeの検証 flagsの検証 signatureの検証 counterの検証と更新 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 90
signatureの検証 Authenticatorの検索 PublicKeyの取得 authenticatorDataのパラメータとclientDataJSONのハッシュ値を連結 AuthenticatorAssertionResponseからsignatureを取得 signatureを検証 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 91
Authenticatorの検索 CredentialIdを元にAuthenticatorを検索する let findAuthr = (credID, authenticators) => { for(let authr of authenticators) { if(authr.credID === credID) return authr } throw new Error(`Unknown authenticator with credID ${credID}!`) } サンプル Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 92
publicKeyの取得 登録時に保存したpublicKeyを取りだす PEM形式に変換する • 26バイトのメタデータ(固定)を付与する • Base64エンコード • 64文字ごとに改行コードをいれる • ヘッダとフッタを入れる Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 93
publicKeyの取得
let ASN1toPEM = (pkBuffer) => {
...
// 26byteのメタデータを追加
pkBuffer = Buffer.concat([
new Buffer.from("3059301306072a8648ce3d020106082a8648ce3d030107034200", "hex"),
pkBuffer
]);
type = 'PUBLIC KEY';
...
// Base64エンコード
let b64cert = pkBuffer.toString('base64');
let PEMKey = '';
for(let i = 0; i < Math.ceil(b64cert.length / 64); i++) {
let start = 64 * i;
// 改行コードの追加
PEMKey += b64cert.substr(start, 64) + '¥n';
}
// ヘッダとフッタの追加
PEMKey = `-----BEGIN ${type}-----¥n` + PEMKey + `-----END ${type}-----¥n`;
return PEMKey
}
サンプル
Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved.
94
signatureの検証
let verifyAuthenticatorAssertionResponse = (webAuthnResponse, authenticators) => {
...
let authenticatorData = base64url.toBuffer(webAuthnResponse.response.authenticatorData);
let authrDataStruct = parseGetAssertAuthData(authenticatorData);
let clientDataHash = hash(base64url.toBuffer(webAuthnResponse.response.clientDataJSON))
// authenticatorDataとclientDataJSONのハッシュ値を連結
let signatureBase = Buffer.concat([
authrDataStruct.rpIdHash,
authrDataStruct.flagsBuf,
authrDataStruct.counterBuf,
clientDataHash
]);
// 登録時に保存した公開鍵を取得
let publicKey = ASN1toPEM(base64url.toBuffer(authr.publicKey));
// navigator.credentials.get()の戻り値のsignatureを取得
let signature = base64url.toBuffer(webAuthnResponse.response.signature);
// 検証
response.verified = crypto
.createVerify('SHA256')
.update(signatureBase)
.verify(publicKey, signature);
}
...
Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved.
サンプル
95
Verify Assertion challenge、origin、typeの検証 flagsの検証 signatureの検証 counterの検証と更新 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 96
counterの検証と更新
authenticatorDataのcounterを検証
let verifyAuthenticatorAssertionResponse = (webAuthnResponse, authenticators) =>
{
...
}
if(response.verified) {
if(response.counter <= authr.counter)
throw new Error('Authr counter did not increase!');
authr.counter = authrDataStruct.counter
}
}
...
Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved.
サンプル
97
Verify Assertion challenge、origin、typeの検証 flagsの検証 signatureの検証 counterの検証と更新 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 98
認証成功!! Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 99
まとめ Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 100
まとめ パスワード認証は利便性と安全性に問題 FIDO認証は公開鍵暗号を応用しパスワード認証の課題を解消 FIDO2プロジェクトにより認証対応プラットフォームの拡大 Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 101
RPのサンプル実装 様々な言語で実装されている Node.js : https://github.com/fido-alliance/webauthn-demo Python : https://github.com/duo-labs/py_webauthn Ruby : https://github.com/cedarcode/webauthn-ruby Go : https://github.com/duo-labs/webauthn Java : https://github.com/Yubico/java-webauthn-server この他にもいっぱい!! https://github.com/apowers313/fido2-server-demo/ Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 102
参考 • FIDO認証の概要説明 https://www.slideshare.net/FIDOAlliance/fido-83445442?ref=https://fidoalliance.org/presentations/ • Webauthn-isig http://slides.com/herrjemand/webauthn-isig • INTRODUCTION TO FIDO ALLIANCE https://www.slideshare.net/FIDOAlliance/introduction-to-fido-alliance-66730790 • MDN web docs WebAuthentication API https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API • Welcome to WebAuthn workshop https://slides.com/fidoalliance/jan-2018-fido-seminar-webauthn-tutorial#/ • Verifying WebAuthn/FIDO2 responses https://medium.com/@herrjemand/verifying-fido2-responses-4691288c8770 • Verifying FIDO-U2F Attestation https://medium.com/@herrjemand/verifying-fido-u2f-attestations-in-fido2-f83fab80c355 • WebAuthn from the relying-party view https://speakerdeck.com/ynojima/webauthn-from-the-relying-party-view Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved. 103
Copyright (C) 2018 Yahoo Japan Corporation. All Rights Reserved.