2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Automatic passkey upgradeに対応していないライブラリで無理矢理対応させてみた

Last updated at Posted at 2024-12-02

Safari18、iOS18から、パスワードマネージャーを使ってパスワードを自動入力してログインした後、そのままサイレントでパスキーを登録してしまうという機能が使えるようになりました。

普通に実装したい方は、geboさんのこちらのブログ記事を参照頂くのがいいと思います。

ところで、Automatic passkey upgradeは、冒頭のとおり、ユーザの操作なく完全サイレントで登録処理が走るため、本来WebAuthnであれば必須である、User Presence(ユーザ存在)の確認もなく、UPフラグがゼロで結果が返却される訳ですが、UVだけでなくUPもゼロというのは今までのFIDO/WebAuthnの世界では前例が無く、この機能に対応していないライブラリでは問答無用でエラーになってしまいます。

なお、UVフラグは、User Verificationを行ったことを示すフラグです。こちらのブログ記事で少し説明しています。

ちなみに、皆さん大好きTypeScriptのWebAuthnライブラリである、SimpleWebAuthnも、10月リリースのバージョン11から、User Presenceの確認を省略できるようになりました。

SimpleWebAuthnのリリースノート

私もGlitchという無料ホスティングサービスで、NodejsとSimpleWebAuthnを使って、この機能を実装してみようと試してみました。ですが、Glitchで対応しているNodejsは非常に古く、バージョン16であり、最新のSimpleWebAuthnは対応していないようです。

仕方が無いので、パスキー作成レスポンスをライブラリに渡す前に、無理矢理UPフラグを1にしてみました。
そのソースがコレです。クライアント(ブラウザ)側、サーバ側、どちらで実行しても大丈夫だと思います。

ね、簡単でしょ?


function forceUPflag(cred){
  if(cred.response.attestationObject.startsWith("o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0")){
    // when the first part of CBOR matches ("fmt":"none","attStmt":{},"authData")
    const attObj = base64url.toBuffer(cred.response.attestationObject);
    let view = new Uint8Array(attObj); 
    let flag = view[62];
    console.log(flag.toString(2).padStart(8, '0'));
    flag = flag | 1 ;// UP flag
    console.log(flag.toString(2).padStart(8, '0'));
    view[62] = flag;
    cred.response.attestationObject = base64url.encode(view);
  }
  return cred;
}

と、急にソースを見せられても、何をやっているのかちんぷんかんぷんだと思うので、もう少しだけ説明させてください。

まず、WebAuthn APIを実行して得られるPublicKeyCredentialオブジェクトの中には、Attestaton Objectというデータが含まれます。Attestation ObjectはCBOR形式のデータになっており、構造は下図の様になっています。

Attestaton Objectの構造(W3C WebAuthn仕様より)
https://www.w3.org/TR/webauthn-3/#attestation-object

我々が操作したいUPフラグが含まれるのは、Attestation Objectの中に入れ子で含まれるAuthenticator Dataの、33バイト目のFLAGSと書いてある部分になります。

ここで、もう一度Attestation Objectの図を見て頂くと、fmt(Attestation Format)とauthData(Authenticatior Data)の間には、attStmt(Attestation Statement)というデータが挟まっていることが分かります。

しかしながら、通常同期パスキーにはAttestation Statementが付与されないため、fmtnone だし、attStmtの中身は空になります.

つまり、Authenticator Dataに行き着くまでのデータ構造は、固定されているということが分かります。
それを簡易的に確認するため、functionの一番最初のif文で冒頭のデータ構造をbase64url文字列のまま比較しています。

Authenticator Dataに行き着いた後も、フラグの前は32byteのRP ID hashで固定されているため、結果的に、フラグは必ずAttestaton Objectの63バイト目となります。

そして、UPフラグは、そのバイトの "the least significant bit"となりますので、ビット演算で1ORして、UPフラグを強制的に立てています。

FIDO認証やWebAuthnを調べたことがある方であれば、もしかしたら、「あれ、UPフラグを改変したら、署名検証に失敗するのでは」と心配される方もいるかも知れません。非常に鋭いです!
ですが、パスキーの登録時、Attestationを利用しない場合は、公開鍵の情報だけが送信され、署名検証は行われないのです。

と、言うことで、Glitchで立てたサーバ上で、無事、Automatic passkey upgradeによるパスキーの登録ができるようになりました。
当たり前ですが、よい子は絶対にマネしないでください。ちゃんとライブラリ側の対応を待ちましょう。

実際に動作している様子

最後に宣伝です!

「パスキーのすべて」という本を、3人で執筆しました。
1月27日発売予定です。

(まだリンク先は仮の書名になっているかもです)

パスキーという言葉が生まれる前のFIDO認証の歴史から、Automatic upgradeをはじめとする、最新のWebAuthn Level3で導入される予定の新機能まで、幅広く取り上げています。

今回、それなりにマニアックな記事を投稿させてもらいましたが、逆に言えば、こんなマニアックなこと以外はほとんどこの本でカバーしているということです。

この記事では、Automatic passkey upgradeを利用する場合のWebAuthn APIの呼び方、UP、UVフラグの詳細や、そもそもAttestationとは何なのかといったことすら説明しませんでしたが、この本で詳しく説明しています。
パスキーの導入を社内で説得したい人も、これから開発しますという人も、幅広く満足頂ける本になっていると自負しています。
すでにAmazonだけでなく、丸善ジュンク堂や、紀伊國屋書店、さらには楽天ブックスヨドバシ.com等で予約できるようになっていますので、是非ぜひ自分用、保存用、布教用に一人3冊お買い求め頂けると幸いです!

おまけ

JavaScriptでバイナリを無理矢理いじくってみた系の記事が好きな人はこちらもご覧ください。

何かを強引に実装してみた系の記事が好きな人はこちらもご覧ください。

個人的に一番書いていて楽しかったのは、やっぱり物理的にモノが動いたときなのですが、あまり閲覧数が伸びなかったので見て頂きたいです。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?