🍉

Chrome128でWebAuthnのHintsを使ってみた

2024/09/07に公開

はじめに

Chrome 128 で WebAuthn で Hints が使えるようになったということで、使ってみたメモです。

https://developer.chrome.com/blog/passkeys-updates-chrome-129

環境

  • 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とは?

パスキーを作成するときにのオプションで、パスキーを作成する場所を制限することができます。platformcross-platform を指定します。

https://www.w3.org/TR/webauthn-3/#enum-attachment

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