WebAuthnの超概要
いろいろ読んで回って分からなくてW3Cのドキュメント見て分からなくてメルカリ様のブログでやっと理解しました。メルカリ様ありがとうございます!
メルカリ様が画像を引用されているMDNも、メルカリ様の後に読んだらわかりやすかった。
W3Cのドキュメントはこちら。
なぜ最初に読んだもので分からなかったかというと、Relying PartyというキーワードでSAMLに引きずられたからだと思います。Asserting Partyは強いて言えば認証器です。
あとWeb Authentication APIはHTTPの上でデータをやり取りする類のAPIではなく、WebAuthnを使うためのライブラリ的な意味でのApplication Programming Interfaceと思ってW3Cのドキュメントを読むと筋が通ります。
WebAuthn ってなんだっけ
WebAuthnは証明書を使った認証の仕組みで、パスワードレスログインを実現することができます。
主な登場人物
Relying Party
WebAuthnをサポートするWeb上のサービス。YahooとかDropboxとかGoogleとか。
Webサーバー側でWebAuthnの利用登録やログインのための通信を処理する口を持っていて、かつブラウザ上でサイトにアクセスしたときにブラウザのWebAuthn機能を使うためのコード (JavaScriptなど) がWebサイトに実装されている必要がある。
Conforming User Agent & WebAuthn Client
WebAuthnをサポートするブラウザ。詳しく理解したい場合は、
User Agent = ブラウザ
WebAuthn Client = ブラウザに実装されているWebAuthn対応機能
Authenticator
認証器。これに証明書 (Private Key) が入っている。Google+Chrome+AndroidではAndroidをAuthenticatorとして使えるようです。(Androidはありますが古すぎて検証できませんでした。)
これは例えばPCのTPMと、証明書の生成など足りない部分をフォローするソフトウェアの組み合わせであっても良い様子。
そのほかW3Cのドキュメントで出てきたもので、後になったら分からなくなるかもしれないもの。
WebAuthn Client Device
WebAuthn Clientが入っているハードウェア。簡単に言うとブラウザがインストールされたデバイス。PCとかスマホとか。
準備: WebAuthn利用登録
ゴールはWebサーバー側が信頼しても良い認証器 (の証明書) 情報を登録すること。SAMLとの違いは、SAMLはIdentity Provider単位の証明書なのに対し、WebAuthnの場合は個人単位の証明書であること。
画像はMDNよりお借りします。
Relying Party (Googleなど) のWebサイトからWebAuthnの利用を開始するためのボタンなどをクリックすると①がサーバーからブラウザに対して送信されます。(ここでは簡略化して、JavaScriptアプリとブラウザを一緒にして説明します。)
①「こういう者 (Relying Party Info) ですが、うちのAさん (user Info) を登録したいので証明書ください、チャレンジコードこれです (challenge)」
受け取ったブラウザはAuthenticatorに情報をリクエストします。
②「こういうWebサイト (Relying Party ID/info) からの依頼で、Aさん (user info) の認証情報が欲しいそうですよ。証明書ください。ハッシュはこれ (clientDataHash) ですよ」
③ Authenticatorはなんらかの方法で、WebAuthn Client Device (PCやスマホ) を操作しているユーザーの本人確認をします。指紋認証とか。OKであればAuthenticatorはサーバーに渡す証明書と対になる保管用証明書 (new keypair) を生成します。サーバーに渡す証明書をPublic Keyとか公開鍵とか呼びます。保管用証明書はPrivate Keyとか秘密鍵とか呼びます。生成したものを使ってこれは本人ですよ、確認しましたというレスポンス (attestation) を作成します。
今度はAuthenticatorから生成した内容をブラウザに返します。
④「本人確認できたで結果送りますよ (attestation)、サーバーに渡す証明書これですよ (new public key)、ちなみにAさんとこのWebサイトの組み合わせにはこちらでID (credential ID) 割り振ったので使ってね」
ブラウザは受け取った情報をWebサーバー側に渡します。
⑤「Aさんの件 (clientData)、結果きましたので送りますね (attestationObject)」
この時のclientDataには①でサーバーから送られてきたチャレンジコードとRelying Party Info が入ってます。attestationDataにはサーバーに渡す証明書 (public key) やCredential ID、attestationを生成したAuthenticatorのIDなどが含まれます。
⑥ 最後にレスポンスを受け取ったサーバーはこの内容を信頼していいか検証して登録します。例えば
- チャレンジコードは最初に送ったものと一致してる?
- こちらから送ったこちらの情報 (Relying Party Info) 合ってる?
- 送られてきた証明書は証明書チェーンの出所 (Root CA) たどって信頼していい? (オレオレ証明書使っていいかどうか問題)
OKなら情報を格納して登録完了。ちなみにユーザーはPCやスマホ上でポチポチっと操作するのと、指紋認証などでAuthenticatorへのアクセスを許可するだけ。
本番: 認証
WebAuthnでログインする流れを見てみます。画像はまたMDNより。
ユーザーがRelying Partyのログイン画面などに行ってログインがトリガーされると、サーバーからブラウザに①が送信されます。
①「ログインリクエストあったので、確認してね。チャレンジコード (challenge) これ使ってね」
ブラウザはこれを受け取ったらAuthenticatorに依頼します。
②「ログインリクエスト来たので処理よろしく。出所はここ (relying party id)、ハッシュはこれ (clientDataHash) 使ってね」
③ 受け取ったAuthenticatorはユーザーの本人確認をして今度はAssertionを作成します。これはclientDataのハッシュとAuthenticator Data (ここにCredential IDなどを含む) から生成し、登録時に生成した保管用証明書を使って署名をしたものです。
証明書の仕組みとして、保管用証明書でデータに署名や暗号化などの細工をしたら、対になるpublic keyで検証したり復号したりできます。
AssertionができたらAuthenticatorはブラウザに渡します。
④「Assertion (authenticatorData, signature) これです、あとよろしく」
ブラウザは今度は受け取ったデータをWebサーバーに返します。
⑤「依頼のログインの件 (clientDataJSON)、Assertion (authenticatorData, signature) これですよ」
clientDataJSONには送ったチャレンジコードやRelying Party Infoが入っています。AssertionのauthenticatorDataにはCredential IDが入ってます。
⑥ 受け取ったサーバーは内容を検証します。例えば
- 送ったチャレンジコードと帰ってきたチャレンジコードは同じだっけ?
- こちらから送ったこちらの情報 (Relying Party Info) は同じだっけ?
- Credential IDは登録時と一緒?
- Assertionの署名は登録時に受け取った証明書 (public key) で検証できる?
問題なければ認証、ログイン完了。
感想
これは面白い!
①から⑥の各ポイントで情報が改変されていないかをチェックしているし、登録時にしか知り得ない情報が認証の際にチェックされているので、これはなかなかにセキュアです。
別記事に書いたSIMハイジャックではショートメッセージを使った二要素認証の弱点が書いてありましたが、これは確かに防げます。
ただし当然ですが、認証が終わってしまえば後はログインセッション管理の話なので、Cookie持っていかれちゃったらやられます。セッションの話はWebAuthnの範囲外。
iOSのブラウザも対応済みになったので、「認証器があれば」遊べる。ここが唯一の問題。
その他の取り扱い注意点は、認証器持ち歩きましょうね、なくさないようにしましょうね、です。
APIで使える?
YesでありNo。
Yes:
- ブラウザベースのクライアントであればもちろん使える。
No:
- ネイティブアプリなんかでは使えない。
- もちろんシステム間連携でも使えない。
今後ネイティブアプリ組み込みライブラリが出てAuthenticatorでの本人確認がなくなったら別ですが。
OAuth, OIDC, WebAuthn
そもそも調べ始めたのはOAuthやOIDCとの関係ってどうなるんだろう、です。結果としては使い分けですね。OAuthは認可、OIDCはSSOがメインであり、その一部にWebAuthnが担う認証 (本人性の証明) がありますが、WebAuthnそのものには認可の機能もSSOの機能もないです。
例えばWebAuthnをOAuthやOIDCのユーザー同意画面前のアイデンティティの確認に使うよとかだったらありそう。
OAuthにあってWebAuthnにないもの
Authorization Code GrantやImplicit Grantにある三者間の認可の構造はWebAuthnにはない。
Scopeを使ってどこからどこまでアクセスしていいですよ、と指定する機能もない。
OAuthの三者間のシナリオとScopeの組み合わせってこういうことです。
ポケモンGO (Niantic社) で遊びたいのでアプリインストールしました。
→ 開いてみたらGoogleアカウントを使ってアカウント作れって言われました。
→ 自分のGoogleアカウントで認証してポケモンGOでアカウント作りました。
このときにGoogleアカウントのID&PWをポケモンGOに渡してしまったら、ポケモンGOのアプリはこのアカウントのメールアドレスどころか、Gmailや連絡先やカレンダーやGoogle Driveや何から何までアクセスできるようになってしまいます。
この問題をOAuthは、パスワードをポケモンGOのアプリに直接渡すことなく、Googleアカウントのメールアドレスや表示名などの基本情報だけを渡すことができるよう、アクセス範囲 (scope) を区切って認証して限定的なアクセス許可を出す仕組みを持っています。
WebAuthnは認証だけなので、こういうシナリオは対象外です。
OIDCとWebAuthn
OIDC (OpenID Connect) はさらにGoogleが実施してくれた本人確認結果を信頼してポケモンGOサーバーにアクセスを許可する仕組みです。
WebAuthnはAuthenticatorの判断は信頼するという意味では一見構造が似ていますが、その結果ログインを許可してくれるのはGoogleだけです。ポケモンGOサーバーへのアクセスを許可するにはGoogle-ポケモンGOサーバー間の信頼関係がどうこうという話が必要になるので、これもWebAuthnの対象外。
参考にさせていただきました
上記理解するのに先人の知恵をお借りしました。
この記事が気に入ったらサポートをしてみませんか?