Slide 1

Slide 1 text

WebAuthn実装してみたー s1290035 会津大学学部一年 しんぶんぶん

Slide 2

Slide 2 text

- ただの人間です - s1290035(学部一年) - AizuGeekDojo SA - Zli運営 - SGG創設者&運営 - コミュニティ活動いろいろ - LINE API実践ガイド LINEログイン章 著者 - 最近やってること) - Nuxt.js、Node.jsを使ったWeb開発 - AWS使ってインフラ構築 - ラズパイとかESP32とかで遊ぶ - アイデンティティ管理、認証系 - 最近CTFと競プロを始めた - ツイ廃 - 保有資格: 漢検3級、応用情報技術者試験 自己紹介 ポートフォリオ(shinbunbun.info) @shinbunbun_ ¥3993 マイナビ出版

Slide 3

Slide 3 text

はじめに - 本資料はWebAuthn勉強中のただの大学一年生が書いたものな ので、間違いがある可能性があります。あらかじめご了承くださ い。 - ちゃんとした知識をつけたい方は是非一次情報(仕様書)を一緒 に読んで勉強しましょう - 本記事はほとんどMDNとW3Cの仕様書に載っている内容なの で、詳しく知りたい方はぜひ仕様書を読んでみてください

Slide 4

Slide 4 text

WebAuthnとは - Web Authenticationの略 - Credential Management API の拡張機能 - ウェブサイトの認証に公開鍵暗号を使用する - →パスワードレス認証やMFAが簡単にできる - Authenticator(認証機)というものがあり、その中で生成された公 開鍵を使って認証する - 例えばWindows HelloとかTouchIDとかUSBトークンとか

Slide 5

Slide 5 text

FIDO2について - W3C WebAuthn, CTAP1, CTAP2という3つの仕様からできてい る - WebAuthn: 前述の通り。これはあくまでブラウザ側の規格 - CTAP: ブラウザと外部認証機間で通信するための仕様 - CTAP1: 従来FIDO U2Fと呼ばれていたもの。FIDO2の規格に対応したブラウ ザ、OSでFIDO U2F対応デバイスが使用できる - CTAP2: FIDO2で新しく定義されたCTAP仕様

Slide 6

Slide 6 text

登場人物 - Server - (だいぶ語弊があるけど)Relying Party(RP)って呼ばれるやつ - クライアントアプリケーションのこと - 今回で言うと僕が作った認証アプリのバックエンド - Browser - u7693(妖精王)の主食 - Rustで書かれてるServoが特に美味しいらしい - Authenticator - 認証機 - 指紋認証機(TouchID的なやつ)とかUSBトークンとかそういうやつ

Slide 7

Slide 7 text

登録(Credential作成)について

Slide 8

Slide 8 text

よくわかんないので実際に触ってみよう

Slide 9

Slide 9 text

Credential作成の流れ https://developer.mozilla.org/ja/docs/Web/API/Web_Authentication_API

Slide 10

Slide 10 text

めちゃ雑に説明すると 0. BrowserからServerに「認証始めたいんだがお前(RP)の情報教えてくれ ん?」ってリクエストする 1. ServerからRPの情報(and more)が返ってくる 2. Browserがその情報とかもろもろ含めてAuthenticatorに「証明書作ってく れや」ってリクエストする 3. Authenticatorがユーザーを認証して鍵ペアを生成する 4. Authenticatorが認証情報をBrowserに返す 5. Browserがいい感じに加工してServerに返す 6. Serverで署名検証とかいろいろいい感じにvalidationする Credential作成完了🎉🎉🎉

Slide 11

Slide 11 text

もうちょいちゃんと説明します

Slide 12

Slide 12 text

Credential作成の説明 1.サーバーに認証開始要求を送る a. この時、サーバからはchallenge, id, rpが返ってくる i. challenge: リプレイアタック防止のために、十分なエントロピーを持ったランダムな文字列(暗号論的 擬似乱数など)を用意する必要がある ii. id: ユーザー固有のid iii. rp: リライングパーティ(クライアントアプリケーション)の情報 2-1. いくつかのパラメータを含めてnavigator.credentials.create()を呼び出す b. 今回のサンプルでは以下のパラメータを使用した c. rpのid, name d. userのid, name, displayName e. publicKeyのtype, alg(アルゴリズム) f. attestation(RPにAuthenticationデータを渡すかどうか) g. timeout時間 h. challenge(前述)

Slide 13

Slide 13 text

Credential作成の説明 2-2. ブラウザが内部的にauthenticatorのauthenticatorMakeCredential()を呼 び出す 3. Authenticatorが新しい鍵ペアと Attestation を作成 a. Attestation: credential IDs, credential key pairs, signature countersなどを含む 認証情報を指す b. 厳密に言えば→PIN の入力や指紋・虹彩認証などを通してユーザー確認をした後 に鍵ペアを生成し、秘密鍵を安全に保存する。もう一方の公開鍵はAuthenticator 固有の秘密鍵で署名される。 4. Authenticatorがブラウザにデータを返す a. 一意のcredential idとattestation情報がブラウザに返され、attestation objectにな る

Slide 14

Slide 14 text

Credential作成の説明 5. PublicKeyCredentialというCredentialデータが作成され、サーバにおくられ る 6. サーバーが検証を行う(検証には主に以下のものが含まれる) a. challenge が送信時と同じものであるかの確認 b. origin が期待された origin となっていることの保証 c. clientDataHash の署名とAuthorizerの証明書チェーンを使った attestation の検証

Slide 15

Slide 15 text

認証(Credential取得)について

Slide 16

Slide 16 text

よくわかんないので実際に触ってみよう

Slide 17

Slide 17 text

認証の流れ

Slide 18

Slide 18 text

めちゃ雑に説明すると 0. BrowserからServerに「認証始めたいんだがお前(RP)の情報教えてくれ ん?」ってリクエストする 1. Serverからchallenge(and more)が返ってくる 2. Browserがその情報とかもろもろ含めてAuthenticatorに「認証してくれ や」ってリクエストする 3. Authenticatorがユーザーを認証して、登録時に生成した秘密鍵を使って 新しい署名を作成する 4. Authenticatorが署名とかもろもろをBrowserに返す 5. Browserがいい感じに加工してServerに返す 6. Serverで署名検証とかいろいろいい感じにvalidationする 認証完了🎉🎉

Slide 19

Slide 19 text

もうちょいちゃんと説明します

Slide 20

Slide 20 text

Credential取得の説明 1.サーバーに認証開始要求を送る a. この時、サーバからはchallengeが返ってくる b. 今回のサンプルではuserのメールアドレスをリクエストに含め、それに対応した CredentialIdを返す処理を書いた 2-1. いくつかのパラメータを含めてnavigator.credentials.get()を呼び出す c. 今回のサンプルでは以下のパラメータを利用した i. rpId ii. allowCredentials(許可されるCredentialのリスト) iii. timeout iv. challenge(前述) 2-2.ブラウザが内部的にauthenticatorのauthenticatorGetCredential()を呼び出 す

Slide 21

Slide 21 text

Credential取得の説明 3. Authenticatorが登録時に生成された秘密鍵を用いて署名を行うことで 新しいAssertionを生成 a. 厳密に言えば→PIN の入力や指紋・虹彩認証などを通してユーザー確認を した後に鍵ペアを生成し、秘密鍵を安全に保存する。もう一方の公開鍵は Authenticator固有の秘密鍵で署名される。 4. AuthenticatorがauthenticatorDataとassertionの署名をBrowserに返す 5. 認証情報がBrowserに返される 6. Serverが検証を行う(主に以下の検証が含まれる) a. rpIdがこのサービスで想定しているものか b. Authenticatorによって署名されたchallengeがserverによって生成されたも のと一致しているか c. 登録要求の際に保管した、Authenticatorによる署名を検証するための公開 鍵が用いられているか

Slide 22

Slide 22 text

もうちょい仕様に踏み込んでみる

Slide 23

Slide 23 text

Credential作成時の検証処理について

Slide 24

Slide 24 text

Credential作成時の検証処理について 1. ※AuthenticatorResponse.clientDataJSONをCとする 2. C.typeがwebauthn.createであることを確認する 3. C.challengeがoption.challengeのbase64url encodeに等しいことを確認する 4. C.originがRPのoriginと一致することを確認する 5. ※hashをCのSHA-256ハッシュ値とする 6. AuthenticatorResponse.attestationObjectをCBOR decodingし、fmt(attestation statement format), authData( authenticator data), attStmt(attestation statement)を取得する 7. authDataのrpIdHashがRPIDのSHA-256ハッシュであることを確認する 8. authData内のflagsのUser Presentビット, User Verifiedビットが1であることを確認する 9. authData内のattStmtのalgがnavigator.credentials.create()のパラメータで指定したalgと一致していることを確認 する 10. authData内のfmtからAttestation Statementのフォーマットを決定する 11. attStmtが正しい証明書であり、署名が有効であることを検証する 12. 検証が成功した場合、Authenticatorのルート証明書を取得する 13. (自己署名証明書でない場合)Attestationの公開鍵がルート証明書に正しくチェーンアップされているかを確認する 14. credentialIdがまだ他のユーザーに登録されていないことを確認する 15. 正常に検証されて信頼できることが判明したら新しいクレデンシャルをアカウントに登録する

Slide 25

Slide 25 text

鍵ペアって毎回作るんですか? by妖精王 - こんな質問があったので簡単に解説します - AuthenticatorはCredentialを作成するたびに鍵ペアを生成し、秘密鍵を安全に保管し ます - 公開鍵はRelying Partyに渡すのですが、その際にAuthenticator”自身”の証明書に紐 づく秘密鍵で署名を行います - Relying Partyは、Attestationの公開鍵がルート証明書に正しくチェーンアップされてい るかということと署名を検証することで、 Authenticatoの信頼性を確認できる - ちなみにルート証明書はFIDO Metadata Serviceなどのレジストリで公開されて おり、AuthenticatorDataのattestedCredentialDataに含まれるaaguidを使用 して取得できる

Slide 26

Slide 26 text

実装で苦労した点とか

Slide 27

Slide 27 text

Goつらぴ - 未経験言語で実装するのマジ辛かった - CBORつらすぎ(後述) - TSでかけばよかった(後悔)

Slide 28

Slide 28 text

base64urlでどハマり - PublicKeyCredential.idがbase64urlで返ってくるため、btoaで ArrayBufferに変換できなかった - base64に変換してからbtoaでArrayBufferにした

Slide 29

Slide 29 text

Authenticator Dataの処理 - ArrayBufferの先頭32バイトがrpIdHash、次の1バイトがflags、次 の4バイトがsignCountといったようになっているため、これらを取 り出さないといけない - 加えてflagsは0ビット目がUser Present、2ビット目がUser Verified...といったようになっているため、シフト演算子を使って1 ビットずつ取り出すのがめんどくさかった

Slide 30

Slide 30 text

CBOR - JSONをバイナリで表現したもの(らしい) - Goでこれ扱うのが難しすぎて諦めた - ライブラリ使ったけどうまく動かなかった

Slide 31

Slide 31 text

署名検証実装できなかった - CBORの件とかいろいろあった結果、署名検証が実装できなかっ た

Slide 32

Slide 32 text

さいごに

Slide 33

Slide 33 text

今後の展望 - とりあえず一連の検証は全部実装する - Resident Keyを使った実装がうまくいかなかったのでリベンジする - これが実装できれば認証時にクライアントに登録されている鍵一覧から選択 できるため、ユーザー名すら入力する必要がなくなる - Federated identity実装してみる(OIDCとか使えるのかも...?) - FIDO2のCTAPさわってみる - LINEのFIDO2 Serverさわってみる - W3Cの仕様書をもっとちゃんと読み込む - WebAuthn以外もいろいろな知識が必要(例えば電子署名とかPKIと か)なので、その辺をもう一回復習する

Slide 34

Slide 34 text

みんなもれっつうぇぶおーすん!