Chrome128でWebAuthnのHintsを使ってみた
はじめに
Chrome 128 で WebAuthn で Hints が使えるようになったということで、使ってみたメモです。
環境
- macOS Sonoma 14
- Chrome 128
Hints について
Hintsとは、WebAuthnでパスキーを作成するときのオプションです。以降は PublicKeyCredentialHints とします。
今までは、パスキーを作成する認証器を制限したい場合、AuthenticatorAttachment を使用してたんですけど、加えてPublicKeyCredentialHints を使うことができるようになった、とのことです。
パスキーを作成する場所はたくさんある
最近はとにかくいろんなところにパスキーを作成できるようになってます。OSやブラウザ、使っている拡張機能などによっていろいろですが、例えば私の環境の場合、以下の場所にパスキーが作成できます。
-
Bitwarden
-
Googleパスワードマネージャ
-
iCloudキーチェーン
-
別のスマートフォン、タブレット、またはセキュリティキー
-
SH-RM15
-
自分の Chrome プロフィール
Bitwarden
Bitwarden
-
私のChromeに入れている拡張機能のパスワードマネージャです。パスキーを作成するときに必ず一番最初に出てきます。
-
ポップアップダイアログの「パスキーを新しいログイン情報として保存」というボタンをクリックするだけでパスキーが作成できます。
-
作成したパスキーは別のデバイスでも使えます。
-
ここにパスキーを作りたくない場合は
x
で閉じると次の選択肢が出てきます。
パスキーを保存する場所を選択
Googleパスワードマネージャ
-
Googleさんのクラウドにパスキーを作成します。
-
macの Touch ID で指紋認証してパスキーを作成します。
-
作成したパスキーは別のデバイスでも使えます。
iCloudキーチェーン
-
Appleさんのクラウドにパスキーを作成します。
-
macの Touch ID で指紋認証してパスキーを作成します。
-
作成したパスキーは別のデバイスでも使えます。
別のスマートフォン、タブレット、またはセキュリティキー
-
これを選択するとQRコードが表示されます。
-
QRコードをスマホでスキャンするとスマホでパスキーを作成します。
-
例えば iPhone でQRコードをスキャンすると、その iPhone で接続している iCloudキーチェーン にパスキーを作成します。
-
画面下に小さく書いてあるように YubikeyのようなUSBセキュリティキー を挿入するとそこにパスキーを作成します。
SH-RM15
-
これは私の持っているAndroidスマホです。
-
chrome://settings/securityKeys/phones を見ると、「Chrome にログインしており、セキュリティ キーとして使用できる」 ってなってます。
-
Androidスマホに認証画面が出てスマホ上で認証してパスキーを作成します。
-
このパスキーは Googleパスワードマネージャ に保存されるようで、別のデバイスでも使えます。
Androidスマホ
自分の Chrome プロフィール
-
Googleパスワードマネージャ に作成されるのかと思いがちですが、違います。Chrome プロフィール というところにパスキーを作成します。
-
macの Touch ID で指紋認証してパスキーを作成します。
-
他と異なる重要なポイントとして、このパスキーはこのmacでしか使えない ということです。
-
作成したパスキーは chrome://settings/passkeys から確認します。
パスキーをどこに作成しているか把握できない問題
というわけで、よくわからないまま気軽な気持ちでパスキーを作成していると、パスキーをどこに作成したかわからない ってことになり ログインするときにパスキー使えない ってことになりがちです。
これを少しでも回避するために、パスキーを作成するときに選択肢を制限することができます。これは個人が設定するのではなく、RP側(アプリ側)で制限します。これを AuthenticatorAttachment や、PublicKeyCredentialHints でやります。
AuthenticatorAttachmentとは?
パスキーを作成するときにのオプションで、パスキーを作成する場所を制限することができます。platform
か cross-platform
を指定します。
platform
操作しているデバイスに内蔵されている認証器でパスキーを作成します。先程の例では以下です。
- Googleパスワードマネージャ
- iCloud キーチェーン
- 自分の Chrome プロフィール
パスキーを作成する際、これ以外の場所の選択肢は出てきません。
cross-platform
操作しているデバイスとは別のデバイスの認証器でパスキーを作成します。先程の例では以下です。
- 別のスマートフォン、タブレット、またはセキュリティキー
- SH-RM15 (セキュリティキーとして利用できるAndroidデバイス)
パスキーを作成する際、これ以外の場所の選択肢は出てきません。
保存先 | AuthenticatorAttachment |
---|---|
Googleパスワードマネージャ | platform |
iCloud キーチェーン | platform |
自分の Chrome プロフィール | platform |
SH-RM15 | cross-platform |
別のスマートフォン、タブレット、またはセキュリティキー | cross-platform |
Bitwarden(Chrome拡張) | platform, cross-platform(*) |
*Chrome拡張はAuthenticatorAttachmentではコントロールできないようです
PublicKeyCredentialHints とは?
で、今回 Chrome で追加された PublicKeyCredentialHints を組み合わせると、さらに細かくコントロールできます。 security-key
, client-device
, hybrid
を指定することができます。
5.8.7. User-agent Hints Enumeration (enum PublicKeyCredentialHints) の説明を要約します。
security-key
物理的なセキュリティキーを推奨するとき。例えば、企業が従業員にセキュリティキーを発行している場合。
client-device
クライアントデバイスのプラットフォーム認証器を推奨するとき。
hybrid
クライアントデバイスのパスキーを使わせたくない、スマートフォンを認証器として推奨する、物理的なセキュリティキーも可能とする場合。
とのことですが、いまいちわからないので、実際に試してみたいと思います。
試してみた
AuthenticatorAttachment
, PublicKeyCredentialHints
の指定を一通り組み合わせて試した結果で、意味のありそうな組み合わだけ説明します。
AuthenticatorAttachment | PublicKeyCredentialHints | 結果 |
---|---|---|
* | * | 何を指定しても Bitwarden のパスキー作成ポップアップ が最初に出てくる |
platform | client-device | iCloud キーチェーンが最優先される |
cross-platform | hybrid | 別のスマートフォン、タブレット、またはセキュリティキー が最優先される |
cross-platform | security-key | USBセキュリティキー が最優先される |
1) 何を指定しても Bitwarden のパスキー作成ポップアップ が最初に出てくる
これはBitwarden特定ということではなく、たぶん Chrome拡張のパスワードマネージャ全般が同じ動きかもしれません。AuthenticatorAttachment
, PublicKeyCredentialHints
に何を指定しても、一番最初にポップアップが出てきます。
オプションでは コントロールできないようです...
2) platform、client-device
私の環境では iCloud キーチェーン のポップアップが最初に出てきました。使っているデバイス内蔵の認証器が最優先になるんでしょうか、使っているデバイスによって動作が変わりそうな気がします。
キャンセルで閉じると、別の場所にパスキーを作成できます、このときの選択肢は platform
で制限がかかっています。
3) cross-platform、hybrid
QRコードが最初にポップアップされるようになります。「別の方法で保存」をクリックすると別の場所にパスキーを作成できます、このときの選択肢は cross-platform
で制限がかかっています。
4) cross-platform、security-key
セキュリティキーを挿入する案内が出てきます。積極的にYubikeyなどのUSBセキュリティキーにパスキーを作成させたいRPはこれを使うとよさそうです。
「別の方法で保存」をクリックすると別の場所にパスキーを作成できます、このときの選択肢は cross-platform
で制限がかかっています。
5) その他の組み合わせ
以下の組み合わせの場合、 PublicKeyCredentialHints
の設定は無視されました。
- platform, security-key
- platform, hybrid
- cross-platform, client-device
まとめ
Hintsは以下のようなケースに使えそうです。
- YubikeyなどのUSBセキュリティキーにパスキーを作成してほしい
- PCではなく、スマホにパスキーを作成してほしい
会社支給のYubikeyやスマホを使ってパスキー認証する場合になるかと思います。が、個人の設定ではなく、RPの設定なので社内システムでパスキーを導入する際に使えそうなオプションかなと思いました。
例えば
- 支給したセキュリティキーやスマホでパスキーを管理する。
- 学校・病院など職員が共有で利用する端末でパスキーを導入する。
ただ、
- Chrome拡張のパスワードマネージャには効かない
- PublicKeyCredentialHints は制限ではなく優先されるだけ。別の場所にパスキーを作ることも可能
という状況なので、絶対セキュリティキーだけ、それ以外はダメ、といったケースは難しそう。
実装メモ
今回 サーバサイドがJava のサンプルアプリを作って検証しました。
PublicKeyCredentialHints は webauthn4j の 0.26.0.RELEASE で対応してました。Yubicoの java-webauthn-server は 最新版の Version 2.5.3 では まだ対応してないようです。
サンプル
webauthn4j を使って パスキーを登録するとき、サーバーサイドで RegisterOption を作成するKotlinのサンプルを晒します。
authenticatorAttachment に cross-platform、PublicKeyCredentialHints に security-key を指定しています。
★★★ authenticatorAttachment ★★★ と ★★★ PublicKeyCredentialHints ★★★ のところだけ参考にしていただければ。
override fun getRegisterOption(userId: String): RegisterOption {
val mUser = mUserRepository.findByUserId(userId) ?: throw RuntimeException("User not found")
val challenge = DefaultChallenge()
val userInfo = PublicKeyCredentialUserEntity(
createUserId(mUser.internalId), // id
userId, // name
mUser.displayName, // displayName
)
// PublicKeyCredentialParameters の指定はどうするのがいいのか今ひとつわからないんですけど、
// WEB+DB PRESS Vol.136 第3章 パスキー実装の基礎知識 のサンプルの通りに指定した
val pubKeyCredParams = listOf(
PublicKeyCredentialParameters(
PublicKeyCredentialType.PUBLIC_KEY,
COSEAlgorithmIdentifier.ES256
),
PublicKeyCredentialParameters(
PublicKeyCredentialType.PUBLIC_KEY,
COSEAlgorithmIdentifier.RS256
)
)
// このユーザーで登録済みのクレデンシャルを設定する
val excludeCredentials = mFidoCredentialRepository.findByUserInternalId(mUser.internalId).map { credential ->
PublicKeyCredentialDescriptor(
PublicKeyCredentialType.PUBLIC_KEY,
credential.credentialId,
null
)
}
val authenticatorSelectionCriteria = AuthenticatorSelectionCriteria(
// ★★★ authenticatorAttachment ★★★
AuthenticatorAttachment.CROSS_PLATFORM,
// requireResidentKey: パスキーとして登録する場合は true を指定すること
true,
// パスキーとして登録する場合は REQUIRED を指定すること
UserVerificationRequirement.REQUIRED
)
// 登録結果(attestation)に署名をつけるかどうかを指定する。
// NONE: 署名無しを指定する
// 署名が付く場合、attestationStatementに署名、formatに署名形式が含まれる
// 署名形式は packed, tpm, android-key など種類があって検証方法も異なるが、ここではNONE指定なので深く考えないことにする
val attestation = AttestationConveyancePreference.NONE
// ★★★ PublicKeyCredentialHints ★★★
val hints = listOf(PublicKeyCredentialHints.create("security-key"))
val option = PublicKeyCredentialCreationOptions(
rp,
userInfo,
challenge,
pubKeyCredParams,
TimeUnit.SECONDS.toMillis(60),
excludeCredentials,
authenticatorSelectionCriteria,
hints,
attestation,
// extensions: 拡張機能は使わないので null を指定する
null
)
return RegisterOption(option)
}
Discussion