OID4VPのSession Fixationについての整理

こんにちは。GMOグローバルサイン・ホールディングスでVerifiable Credentialsの研究をしている開発者の神沼@t_kanumaです。

本稿は、以下の記事の続編です:

2024年の分散型アイデンティティ領域の潮流の考察とKERIについて

上記記事の中で、現在のメインストリームにおいてはWallet Attestationがホットトピックであると述べましたが、それと並行して、リモート提示におけるVerifierからHolderへのCredential提示要求に対し、User AgentとWalletがどう連携するかという点も、非常に重要なテーマになっていると思います。

この点については、EUDIW ARF、ISO 18013-7、OIDF OID4VP、OIDF OID4VC HAIPといった文書内でも、現在標準化が進められている新しい仕組みであるDigital Credentials API(およびOID4VP over Digital Credentials API)に関する事項を含めて、一定の記載があります。例えばEUDIW ARFでは、以下の通り、Digital Credentials APIの標準化が完了したらその利用が必須になるものの、現在はオプションであり、カスタムURLスキームは引き続き許容されるという内容になっています。

The use of this API by Wallet Units and Relying Parties is optional, and custom URL schemes may be used as well. If a Wallet Unit implements a custom URL scheme, it will need to implement mitigations for the challenges described in this section.
EUDIW ARF v2.0.0: 4.4.3 Remote presentation flowsより

Wallet Units and Relying Party Instances SHALL support the W3C Digital Credentials API for remote presentation flows, provided that a) this API is fully standardised, b) this API complies with the expectations outlined in Chapter 3 of the Topic F discussion paper, and c) this API is broadly supported by relevant browsers and operating systems.
EUDIW ARF v2.0.0 Annex 2: A.2.3 High-Level Requirements: OIA_08より

カスタムURLスキームには、UXとセキュリティの両面で課題があると理解していますが、セキュリティ面においては、OID4VP(draft 29)に記載の「14. Security Considerations – 14.2 Session Fixation」の章が、このトピックを理解する上での出発点として重要だと考えています。ただ、この仕様上の説明だけでは、少し抽象度が高いと感じ、私自身すんなりと理解できなかったため、もう少し具体的なシステム構成や方式設計を仮定し、思考した内容を以下に共有します。(本稿は「Session Fixation」の章を一読された方を前提にしています。ご了承ください。)

内容に誤解や不備などがあれば、ぜひコメント等でご指摘いただけますと幸いです。

攻撃の概要

攻撃者は、Relying Party(RP)/Verifierのサイト上でVP提示セッションを開始し、カスタムURLを取得します。攻撃者はこのURIを被害者にメールやチャットなどで送信します。被害者がそれをWalletで読み込み、提示に応じると、Authorization ResponseがRPに送られます。しかしそのResponseが、もともと攻撃者が開始した提示セッションに紐づいてしまう、言い換えれば固定化されてしまい、最終的に攻撃者が認証や認可された状態になってしまう、というのがSession Fixationの問題という理解でいます。

仮定するシナリオと方式設計

ここでは、シンプルなSPA(Single Page Application)構成を基に以下の設計を想定します。

RPバックエンドは、何らかの方法でログイン認証を行い、アプリの利用を許可するJWTを発行します。RPフロントエンド(User Agent)は、そのJWTを自身(ブラウザ上のLocal StorageまたはCookie)に保持し、バックエンドAPIの呼び出しに際しその認可を受けるという典型的な動作をします。

ユーザがアプリを操作して、VP提示セッション開始画面にたどり着くと、RPバックエンドはVP提示セッションを管理する識別子とAuthorization Requestを生成します。このセッション識別子文字列をTransaction IDと呼ぶことにします。このTransaction IDは一時的なVP提示の文脈を維持するためのアプリケーション状態を表すものとして、RESTfulな設計上フロントエンド側に保持します。(また、Transaction IDは上記のバックエンドAPI認可JWTとは独立した一時的・操作単位の識別子であり目的が異なるため、関心の分離のため、別々に管理する設計とします。)

Request/Responseペアの識別子は、実質それらに含まれるnonce(またはstate)を利用できる理解です。そしてRPバックエンドのDB上でTransaction IDとnonceを対応ずけて管理します。(OID4VPにおいてVP提示セッションの作成や完了のプロセスは仕様の外側にあり、この部分の設計は実装者側に委ねられている部分だと理解しています。)

VP提示方式の細かいパターンは後述しますが、最終的にRPバックエンドは、RPフロントエンドからTransaction IDとAuthorization Response(nonceまたはstateを含む)を受け取り、その紐付けを確認することで、「このVP提示が元々のセッションを開始したユーザから行われたものであるか」を確認します。

こういった実装を仮定したときに問題となるのが、提示パターンのケースによってはSession Fixationが起きてしまうことです。では、ここから詳細に提示方式を場合分けして見ていきます。

VP提示方式の場合分け

Same DeviceとCross Deviceの2つの環境、そしてFragment Mode、Direct Post Mode(OID4VPに記載のSession Fixation対策有り)、Direct Post Mode(Session Fixation対策無し)の3つのモードよる計6つのパターンにおいて、攻撃されるケースと正常なケースの成否を考えます。

ここでの「Same Device」「Cross Device」とは、攻撃者側ではなく、被害者(VP提示を行う側)のデバイス構成を指します。

ポイントは2つあります。1つ目は「攻撃は成立しないが、正常ケースは通る」という状態を実現できるかどうかを判断するということです。2つ目は「Direct Postの場合、Responseがどのセッションから来たものかをRPが直接的に判別できない」という技術的制約があるということです。(それ故にSession Fixation対策が施される。)

以下に整理した表から先に結論を述べると:

  • Same Device環境では、Fragment ModeあるいはDirect Post Modeのどちらを使っても、正常に防御が成立します。
  • 一方、Cross Device環境では、「攻撃を防ごうとすると正常ケースも通らない」「正常ケースを通そうとすると攻撃も成立してしまう」という結果になり、カスタムURLスキームによる連携では課題が残ってしまいます。ここはOID4VP over Digital Credentials APIにて解決される認識です。(それが実装されるまでCross Deviceの利用は禁止にするのも1つ選択肢なのかもしれません。)
No モード デバイス環境 攻撃ケース 正常ケース 結果 備考
1 Fragment Mode Same Device 成立しない 成立する OK
2 Fragment Mode Cross Device 成立しない 成立しない NG 正常ケースが通らない
3 Direct Post(対策無) Same Device 成立する 成立する NG 攻撃が成立してしまう
4 Direct Post(対策無) Cross Device 成立する 成立する NG 攻撃が成立してしまう
5 Direct Post(対策有) Same Device 成立しない 成立する OK
6 Direct Post(対策有) Cross Device 成立しない 成立しない NG 正常ケースが通らない

ケースごとの詳細な説明

ケース No.1,2 : Fragment Mode + Same Device or Cross Device

被害者はWalletからRPフロントエンド上で、Authorization Responseを受け取り、それをバックエンドに送信します。FragmentとしてWalletからブラウザにリダイレクトされる形式であり、フロントエンドはAuthorization Responseに加えて、保持していたTransaction IDもあわせて送信する実装になると考えます。このとき、フロントエンド上にTransaction IDが存在しない(提示セッションが存在しない)とすると、そもそもバックエンドへ送信できない設計になっているはずです。また仮に送信してもバックエンドがそれを受け入れない設計になっているはずです。したがって、攻撃は成立しません。仮に、被害者がTransaction IDを保持していたとしても、そのIDはResponseには紐づいていない別セッションであるため、バックエンドでの検証が失敗すると考えます。(バックエンドまで送信されてしまう実装では強制提示が成されているという認識もできますが、いずれにしてもDirect Postと異なりセッションコンテキスト内でResponseが送信されるFragementモードではSession Fixation攻撃自体は起きません。)

一方、正常なユースケースにおいて、Same Deviceでは、ユーザが自身のセッション上でAuthorization Responseを(Transaction IDとと共に)送るため、問題なくVP提示が完了します。また、Cross Deviceにおいて正常ケースが成立しないことは自明であり割愛します。

ケース No.3、4: Direct Post Mode + Same or Cross Device

被害者のWalletはAuthorization Responseを直接RPのバックエンドにPOSTします。Walletから直接送信するということは、セッションが所在するフロントエンド(User Agent)から一度そのコンテキストを離脱しているということであり、必ずセッションコンテキスト上からResponseが送信されるFragmentモードとは異なり、Direct Postモードでは、RPはそのResponseがどのセッションをオリジンにしてきたのかを判別する術がありません。(また仮にTransaction IDをAuthorization ResponseのPayloadに含めたとしても、それはWallet側で任意に指定された情報であり、セッションオリジンの証明にはならず、何の信用も置けない情報です。)

この状態において、以下のような攻撃が成立する認識です。攻撃者は、自身のUser AgentでVP提示セッションを開始し、Transaction IDを保持し続けてポーリングを続けます。被害者がVPを提示すると、RPバックエンドは受け取ったAuthorization Responseと、攻撃者が保持するTransaction IDの紐付けを「成立したもの」として認識してしまう可能性があります。その結果、攻撃者が本来被害者のものである提示結果をもとに、次のアクションに進めてしまうことが起こり得ます。

このケースでは、正常なVP提示も成立しますが、それと同時にSession Fixationという脆弱性が成立してしまうことになります。正常ケースを成立させるためにRP側がReponseとは別経路でTransaction ID単体を受け取る方式を許容する設計にせざるを得ないからです。

ケース No.5: Direct Post Mode + Same Device

このケースでは、Session Fixation対策として、WalletがAuthorization ResponseをRPバックエンドに対してdirect postした後、RPバックエンドがWalletにResponse Codeを返却し、Walletはそのコードを使って指定されたRedirect URIへリダイレクトを行うという仕組みが採られます。

このリダイレクトの過程で、フロントエンド(User Agent)は、保持していたTransaction IDとともに、RPバックエンドにResponse Codeを送信します。RPバックエンドは、事前に保持していたセッション情報を用いて、Transaction IDとnonceの対応関係を確認し、そのセッションに対して発行されたAuthorization Requestに基づいたAuthorization Responseと、それに対応するResponse Codeの整合性を検証する実装になると考えます。

この検証によって、RPは「このResponse Codeを受け取った」ということから「実際にdirect postを行ったのがこのユーザである」ことを確認できます。また同時に、Transaction IDを保持していたという点から「この提示セッションを開始した者である」ことも確認できます。つまりTransaction IDとResponse Code両方を知っていることで、「提示セッションを開始した者」かつ「実際に提示(direct post)した者」であるいう証明が成立し脆弱性を防ぎます。

そして攻撃者は通常このResponse Codeを知る術を持ちません。一方、正常ケースではユーザは両方を保持することができます。このようにして攻撃ケースは成立せず、正常なユースケースのみが成立する構成となります。

ケース No.6: Direct Post Mode + Cross Device

No.5と同様、攻撃者はResponse Codeを取得できないため、攻撃ケースは成立しません。一方、正常ケースにおいては、Transaction IDとResponse Codeの所在が異なるデバイスに分かれてしまい(例えば、Transaction IDはユーザのPC上にあり、Response Codeはユーザのモバイルデバイス上にある)、この2つを同時にバックエンドに送ることができません。このように、Cross Device構成においては、このResponse Code対策がそもそも成立しないことになります。これはOID4VPの文書内でも以下のように述べられています。

Note that this protection technique is not applicable to cross-device scenarios because the browser used by the Wallet will not have the original session.
OID4VP 14.2 Session Fixationより

最後に

OID4VP over Digital Credentials APIでは、Same Device + Direct Post Modeの構成において、Response Codeのような手段を使わなくとも、ブラウザからWalletへOSレベルでセッションを識別する情報を安全に渡すことで、Direct Postの送信元がセッションを開始したユーザーかどうかを、RPが判別できる仕組みであるいう予想をしています。またCross Device構成においても、User AgentとWallet間で近接通信をすることで、同様に安全にセッション情報を引き継いで解決するという予想をしています。

これについては、以下の文書を読みこれから理解を深めていきたい所存です。

本稿はこれにて終わります。最後までお読みいただきありがとうございました。