Upgrade to Pro — share decks privately, control downloads, hide ads and more …

WebAuthn実装してみたー

 WebAuthn実装してみたー

Yuto Takamune

August 22, 2021
Tweet

More Decks by Yuto Takamune

Other Decks in Programming

Transcript

  1. - ただの人間です - s1290035(学部一年) - AizuGeekDojo SA - Zli運営 -

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

    ウェブサイトの認証に公開鍵暗号を使用する - →パスワードレス認証やMFAが簡単にできる - Authenticator(認証機)というものがあり、その中で生成された公 開鍵を使って認証する - 例えばWindows HelloとかTouchIDとかUSBトークンとか
  3. FIDO2について - W3C WebAuthn, CTAP1, CTAP2という3つの仕様からできてい る - WebAuthn: 前述の通り。これはあくまでブラウザ側の規格

    - CTAP: ブラウザと外部認証機間で通信するための仕様 - CTAP1: 従来FIDO U2Fと呼ばれていたもの。FIDO2の規格に対応したブラウ ザ、OSでFIDO U2F対応デバイスが使用できる - CTAP2: FIDO2で新しく定義されたCTAP仕様
  4. 登場人物 - Server - (だいぶ語弊があるけど)Relying Party(RP)って呼ばれるやつ - クライアントアプリケーションのこと - 今回で言うと僕が作った認証アプリのバックエンド

    - Browser - u7693(妖精王)の主食 - Rustで書かれてるServoが特に美味しいらしい - Authenticator - 認証機 - 指紋認証機(TouchID的なやつ)とかUSBトークンとかそういうやつ
  5. めちゃ雑に説明すると 0. BrowserからServerに「認証始めたいんだがお前(RP)の情報教えてくれ ん?」ってリクエストする 1. ServerからRPの情報(and more)が返ってくる 2. Browserがその情報とかもろもろ含めてAuthenticatorに「証明書作ってく れや」ってリクエストする

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

    3. Authenticatorがユーザーを認証して、登録時に生成した秘密鍵を使って 新しい署名を作成する 4. Authenticatorが署名とかもろもろをBrowserに返す 5. Browserがいい感じに加工してServerに返す 6. Serverで署名検証とかいろいろいい感じにvalidationする 認証完了🎉🎉
  9. 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()を呼び出 す
  10. Credential取得の説明 3. Authenticatorが登録時に生成された秘密鍵を用いて署名を行うことで 新しいAssertionを生成 a. 厳密に言えば→PIN の入力や指紋・虹彩認証などを通してユーザー確認を した後に鍵ペアを生成し、秘密鍵を安全に保存する。もう一方の公開鍵は Authenticator固有の秘密鍵で署名される。 4.

    AuthenticatorがauthenticatorDataとassertionの署名をBrowserに返す 5. 認証情報がBrowserに返される 6. Serverが検証を行う(主に以下の検証が含まれる) a. rpIdがこのサービスで想定しているものか b. Authenticatorによって署名されたchallengeがserverによって生成されたも のと一致しているか c. 登録要求の際に保管した、Authenticatorによる署名を検証するための公開 鍵が用いられているか
  11. 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. 正常に検証されて信頼できることが判明したら新しいクレデンシャルをアカウントに登録する
  12. 鍵ペアって毎回作るんですか? by妖精王 - こんな質問があったので簡単に解説します - AuthenticatorはCredentialを作成するたびに鍵ペアを生成し、秘密鍵を安全に保管し ます - 公開鍵はRelying Partyに渡すのですが、その際にAuthenticator”自身”の証明書に紐

    づく秘密鍵で署名を行います - Relying Partyは、Attestationの公開鍵がルート証明書に正しくチェーンアップされてい るかということと署名を検証することで、 Authenticatoの信頼性を確認できる - ちなみにルート証明書はFIDO Metadata Serviceなどのレジストリで公開されて おり、AuthenticatorDataのattestedCredentialDataに含まれるaaguidを使用 して取得できる
  13. 今後の展望 - とりあえず一連の検証は全部実装する - Resident Keyを使った実装がうまくいかなかったのでリベンジする - これが実装できれば認証時にクライアントに登録されている鍵一覧から選択 できるため、ユーザー名すら入力する必要がなくなる -

    Federated identity実装してみる(OIDCとか使えるのかも...?) - FIDO2のCTAPさわってみる - LINEのFIDO2 Serverさわってみる - W3Cの仕様書をもっとちゃんと読み込む - WebAuthn以外もいろいろな知識が必要(例えば電子署名とかPKIと か)なので、その辺をもう一回復習する