HN 日本語サマリー

← 一覧へ戻る
プログラミング

不透明で相互運用可能なパスキーレコード(Go API付き)

Opaque, Interoperable Passkey Records (and a Go API) (words.filippo.io)

37 pointsby gnabgib6 コメント

要約

この記事では、フィッシング攻撃に対するパスキーの有効性を強調し、サーバーサイド実装を簡素化するために、PHC文字列にインスパイアされた、標準化された不透明で相互運用可能なパスキーレコードのエンコーディング形式を提案しています。この提案は、WebAuthn仕様に基づき、アプリケーションがパスキーレコードをパスワードハッシュのように扱えるようにすることを目指しており、Go言語での実装例も示されています。

全文翻訳

2026年7月20日 不透明で相互運用可能なパスキーレコード(Go API付き) パスキーは、現在情報セキュリティにおいて最も重要な出来事です。なぜなら、フィッシング攻撃に対する圧倒的な有効性に対する唯一の原則的な解決策だからです。メモリ安全性(memory safety)がメモリ破損攻撃に対する唯一の原則的な解決策であるのと同様です。残念ながら、サーバーサイドでの実装は、パスワードハッシュの使用よりも複雑に見えることがあります。このことの一部は避けられませんが、相互運用可能なパスキーレコードエンコーディングを定義することで、もう少し効果的に抽象化できる部分もあります。 WebAuthn仕様は、type、id、publicKey、backupState、transportsなどのコンポーネントを持つ抽象的な概念としてクレデンシャルレコードを定義しています。Googleは、クレデンシャルIDを主キーとし、public_key、backed_up、transportsカラムを持つデータベーステーブルを推奨しています。Adam Langleyの優れたTour of WebAuthnも同様に、cred_idを主キーとし、separate public_key_spkiとbacked_upカラムを推奨しています。すべてのガイダンスは、WebAuthn認証を処理するためにライブラリを使用することを推奨していますが、それでもアプリケーションには相互運用性のない可能性のあるデータベーススキーマが残ります。ライブラリやフレームワークに認証フロー全体とデータベースインタラクションを委ねることが非現実的である場合もあります。アプリケーションがパスワードハッシュのように不透明な文字列として扱える、相互運用可能で十分に仕様化されたパスキーレコードは、中間層の抽象化レイヤーとなり得ます。 c2sp.org/passkey-recordは、Password Hashing Competition(PHC)文字列の構文を借用し、認証子データエンコーディングを大部分の作業に再利用する仕様提案です。レコードは次のようになります: $webauthn$v=1$transports=hybrid+internal$<base64 authenticator data> ペイロードは認証子データであり、WebAuthnによって既に定義され、AuthenticatorAttestationResponseのJSONエンコーディング(アテステーションが使用されていない場合でも navigator.credentials.create() の戻り値)に含まれるクレデンシャルレコードフィールドのほとんどのCTAP2 CBORエンコーディングです。Transportsは唯一欠けているフィールドであり、PHCパラメータとして格納されます。アプリケーションは、ユーザーアカウントに関連付けられているパスキーレコードを追跡する責任を負うだけです。これは、パスワード認証の実装に似ているため、Web開発者には馴染みのあるタスクです(ただし、アカウントごとに複数のパスキーがあります)。 これらの不透明な文字列は、ログインアサーションを検証するため(または適切なexcludedCredentialsで登録リクエストを生成するため)にライブラリに渡すことができます。十分に仕様化された相互運用可能なストレージ形式があれば、クレデンシャルデータベースを維持しながら、パスキーライブラリ(あるいはバックエンド言語)を切り替えることが可能になることが期待されます。 保存したいその他のフィールド パスキーレコードに加えて、アプリケーションは、ユーザーが選択したニックネームや作成日時、最終使用日時などのメタデータフィールドを、見栄えの良いパスキー管理UIを提供するために保存したい場合があります。これらのフィールドのいずれも、WebAuthnライブラリによる特別な処理を必要としません。唯一の例外は、バックアップ状態フラグです。パスキーは、バックアップされているかどうか(例:iCloud KeychainやGoogleアカウントへのバックアップ)をサーバーに報告し、サーバーはそのシグナルを使用してアカウントからパスワードを削除することを提案できます。このフラグはログインごとに変化する可能性がありますが、パスキーレコードは不変であるため、別途保存し、ログインごとに更新する必要があります。平均的なウェブサイトにとっては、このロジックは過大評価されていると思います。なぜなら、それらのウェブサイトは結局のところ、メールパスワードリセットをサポートし続けるからです。 潜在的な暗号/パスキーAPI これらのパスキーレコードの上に構築して、私は潜在的な暗号/パスキーステートレスGoパッケージAPIをドラフトしました。 登録フローは、ログイン中の(またはその他の識別された)ユーザーの詳細と既存のパスキーレコードで RelyingParty.NewRegistration を呼び出し、返されたJSONを parseCreationOptionsFromJSON() に渡し、次に navigator.credentials.create() に渡します。返されたJSONエンコードされたPublicKeyCredentialを RelyingParty.Register に渡し、返されたパスキーレコードをデータベースに保存します。 ログインフローは、ログインページを生成する際に RelyingParty.NewLogin を呼び出し、返されたリクエストを RequestID(request) の下で短いTTLを持つキーバリューストアに保存し、返されたJSONを parseRequestOptionsFromJSON() に渡し、次に navigator.credentials.get() に渡します。返されたJSONエンコードされたPublicKeyCredentialを Inspect に渡し、返されたrequestIDを使用してキーバリューストアからリクエストを取得し、返されたuserIDを使用してデータベースからパスキーレコードを取得します。JSON PublicKeyCredential、リクエスト、およびパスキーレコードを RelyingParty.Login に渡します。 アプリケーションは、永続的でプライバシーを保護する不透明なユーザーIDを各ユーザーに関連付け、ユーザーに関連付けられたパスキーレコードを保存し、RelyingParty.NewLogin によって生成されたリクエストチャレンジをキャッシュする責任を負います。ライブラリは、 parseCreationOptionsFromJSON() および parseRequestOptionsFromJSON() に直接渡すことができるJSON値を生成し、PublicKeyCredentialのJSONエンコーディングを呼び出した結果のJSON値を受け入れます。これは、検出可能なクレデンシャルフロー(別名パスキー、認証子がサーバーにユーザーIDを提供する場合)に最適化されていますが、RelyingParty.NewLoginForUserメソッドは、セカンドファクターフローや再認証プロンプトにも使用できます。APIは、モーダルUIと条件付きUI(オートフィル)フローの両方で機能します。パスキーレコード(AAGUID、BackedUp)およびJSONエンコードされたPublicKeyCredential(ResponseBackedUp)から情報を抽出するためのヘルパーがいくつかあります。 現在、実装はありません。Go 1.28の提案を行う前に、パスキーレコード形式とGo APIについてフィードバックを得たいと考えています。 重複するクレデンシャルIDについて このストレージモデルではできないことの1つは、異なるアカウントが同じクレデンシャルIDを持つパスキーを共有しないことを保証することです。仕様では、これをSHOULD(行うべき)としています。このチェックの理由は、攻撃者が意図的に衝突するクレデンシャルIDを自分のアカウントを通じて注入した場合に、そのIDでクレデンシャルを検索して間違った公開鍵またはユーザーIDにたどり着く攻撃を回避することです。インデックスが存在しない限り、この攻撃は発生しません。インデックスは、インデックスの存在によって導入された攻撃を軽減するためだけに必要です。ログイン試行ではユーザーIDが渡され、それを使用してユーザーのパスキーレコードを検索してログインを検証する場合、他のユーザーが同じクレデンシャルIDを持つパスキーを持っていても問題ありません。これは、2人のユーザーが同じパスワードを共有していても問題ないのと同じです。攻撃者にあなたのPRIMARY KEYを決定させないようにすれば、PRIMARY KEYの衝突攻撃は発生しません。 より多くのGo APIプレビューについては、Bluesky(@filippo.abyssdomain.expert)またはMastodon(@filippo@abyssdomain.expert)で私をフォローしてください。 今年のCENTOPASSI(慎重な計画、100の座標、3日半で1700kmの二次道路を走行するGPS追跡バイクレース)からの写真です。カステル・デル・モンテ(AQ)の眺めです。私の仕事は、プロのGoメンテナーの組織であるGeomysによって可能になっています。GeomysはAva Labs、Teleport、Datadog、Tailscale、Sentryから資金提供を受けています。リテイナー契約を通じて、彼らは私たちのオープンソースメンテナンス作業の持続可能性と信頼性を確保し、私の専門知識および他のGeomysメンテナーの専門知識への直接的なアクセスを得ています。(Geomysの発表で詳細をご覧ください。)以下は、それらの企業からの言葉です! Teleport — 過去5年間、攻撃や侵害は、従来のマルウェアやセキュリティ侵害から、ソーシャルエンジニアリング、クレデンシャル盗難、またはフィッシングを使用して有効なユーザーアカウントやクレデンシャルを特定し、侵害することへと移行しています。Teleport Identityは、アクセスモニターを通じて弱いアクセスパターンを排除するように設計されています。