FIDO認証できるユーザーをスキャンさせない設計を考える
ritouです。
背景
これまで数年に渡り、「新規登録/ログイン時に登録状態がわかるレスポンスを返してくれるな。」というお話をしてきました。
SMSやEmailを入力して認証コードなどを送るタイプのログイン方法でも同様の実装は可能であり、表向きは「認証コードを送信したよ。受け取った値を入れてくれよな。」としつつ実際は既に登録済みだからログインからこい、未登録だから新規登録から来い見たいな内容を送りつつ、画面では正規のユーザーと同様のトークンなりを送り検証が絶対通らん! みたいなのを実装したりしています。
ではこれをWebAuthnなりネイティブ機能で提供されるFIDO認証でこれを実現したいとなった時にどうしたら良いか、細かく考えたらやっぱりめんどいなって話をします。
想定する環境
- パスワード認証と組み合わせる2FA用途ではなく、FIDO認証のみでログインさせる
- 最初にユーザー識別を行うかどうかまでは決めない
- 既にAuthenticatorが登録されているユーザーにはWebAuthnでログインさせたい
- Authenticator未設定のユーザーにもWebAuthnでログインさせるそぶりを見せたい
みたいなところです。
何も考えずにSPA + バックエンドみたいな構成で通常の認証方式と並列でFIDOログインを実装しようとした場合、有効なAuthenticatorが合ったらバックエンドがそのIDなりを返してSPAがWebAuthnの認証を要求するみたいな設計になります。
例えばYahoo! JAPANの場合、(詳しくどのリクエストかは書きませんが)電話番号やメールアドレス、Yahoo! JAPAN IDを最初にバックエンドに送るとどの認証方式が利用できるかみたいなのが返されてそれに応じた認証フローの分岐が行われているようです。
当然、このような設計はユーザー登録済みかつFIDO Authenticator登録済みの強いユーザーの識別子がスキャン可能なわけですが、これを許可したくない場合にどのような設計が取れるのでしょうか。
Client-side discoverable Public Key Credential Source を利用
むかーし、Resident Keyとかusername-lessとか言われてたやつを使えば、RPは最初にユーザーの識別をする必要がありません。
一言で言うとRPは "こいつ誰かわかんないけどとりあえずWebAuthnで頼む" ができるわけです。
SignIn with なんとかでとりあえずIdPに送っちゃう見たいなのと似ていますね。
環境、認証器的にこれが十分に普及しているならこれでいいだろっていう感じはありますが、状況としてはもう少し先なのかなと思ってます。
未設定ユーザーの場合、ダミーの公開鍵を allowCredentials で指定
となると、最初にユーザー識別を行なった上で永遠に成功しないWebAuthnの認証を要求するしかないですね。
登録済みかつAuthenticator設定済みのユーザーの場合はこれでいいです。
- フロントエンドはユーザー識別子相当をバックエンドのAPIに送る
- バックエンドは登録済みのAuthenticator(のCredentialID)一覧を返す
- フロントエンドはallowCredentialsに指定する
それに対して、サービスへの未登録ユーザー、Authenticator未登録ユーザーの識別子を受け取る場合は以下のような処理を行います。
- フロントエンドはユーザー識別子相当をバックエンドのAPIに送る
- バックエンドはダミーのAuthenticator(のCredentialID)一覧を返す
- フロントエンドはallowCredentialsに指定する
万が一実在するCredentialIDによる認証が要求されたとしても4のバックエンドの処理でユーザーとの紐付けの検証でエラーになるはずなので大丈夫そう。
認証コード送信と同様に、SMSやメールで「お前はFIDO認証を試みたがAuthenticatorが登録されてないよ」みたいなのを裏で送ることも可能です。
これでざっくり正規のユーザーとそうではないユーザーで挙動の違いは小さくなる気はするものの、厳密には
- 言うても意識が高く複数のAuthenticatorを登録しているユーザーを基準にするとこれでも差異は出てしまう
- ダミーのCredentialIDの値を毎回同じにしておかないとAPIレスポンスを複数回叩くことで結局スキャン可能
みたいなところがあるので悩ましいです。
まとめ
- ダミーのCredentialIDを使う案はよく考えると厳しい
- Client-side discoverable credentials のサポート待つしかないか
困りましたね。
ではまた!
Discussion