| RFC 10017 | ブラウザベースアプリケーション向け OAuth 2.0 | 2026年8月 |
| Parecki ほか | 現行のベストプラクティス | [ページ] |
この仕様では、OAuth 2.0 を使用するブラウザベースアプリケーションを開発する際に考慮しなければならない脅威、攻撃による影響、セキュリティ上の考慮事項、 およびベストプラクティスについて 詳述します。¶
このメモは、インターネットにおける現行のベストプラクティスを文書化したものです。¶
この文書は、インターネット技術タスクフォース (IETF) の成果物です。これは IETF コミュニティの総意を表すものです。この文書は 公開レビューを受け、インターネット技術運営委員会 (IESG) によって公開が承認されています。BCP に関する詳細情報は、 RFC 7841 のセクション2で確認できます。¶
この文書の現在の状態、正誤情報、および この文書に関するフィードバックの提供方法については、 https://www.rfc-editor.org/info/rfc10017で確認できます。¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
この仕様では、ブラウザ内で実行されるアプリケーションに OAuth 2.0 クライアントを実装するためのさまざまなアーキテクチャパターンについて説明します。また、 ブラウザベースアプリケーションにおけるセキュリティ上の課題を概説し、さまざまなパターンがこれらの 課題への対処にどのように役立つかを分析します。¶
この文書は、OAuth クライアントとして動作する JavaScript フロントエンドアプリケーション (セクション 1.1([RFC6749])で定義) に焦点を当て、 認可サーバー (セクション 1.1([RFC6749])) とやり取りして、 アクセストークン、および必要に応じてリフレッシュトークンを取得します。クライアントはアクセストークンを使用して、 リソースサーバー上の保護されたリソース (セクション 1.1([RFC6749])) にアクセスします。 OAuth を使用する場合、クライアント、認可サーバー、およびリソースサーバーは、それぞれが同じ主体によって 所有または運用されているかどうかにかかわらず、すべて独立した当事者と見なされます。¶
多くの Web アプリケーションは、共通のドメインで動作するフロントエンドと API から構成されており、 OAuth 2.0 に依存しないアーキテクチャを使用できます。これについては、 セクション 7.1で詳しく説明します。このようなシナリオでは、 フェデレーションによるユーザー認証に OpenID Connect [OpenID]を使用でき、 その後、アプリケーションがユーザーの認証状態を維持します。このようなシナリオ (OpenID Connect の基礎となる仕様としてのみ OAuth 2.0 を使用するもの) は、 この仕様の範囲外です。¶
OAuth 2.0 と OpenID Connect を使用するネイティブアプリケーション開発者向けに、これらの技術の 統合方法を示す IETF BCP (現行のベストプラクティス) が公開されました。 この文書は正式には [RFC8252]または BCP 212 と呼ばれますが、開発者がこれらのプラクティスを採用するのを支援する OpenID Foundation 後援のライブラリ群にちなんで、一般に「AppAuth」と呼ばれています。 [RFC8252]は、ネイティブ アプリケーションに OAuth クライアントを安全に実装する方法について、必要に応じて追加の OAuth 拡張機能を 組み込むことを含む具体的な推奨事項を示しています。¶
この仕様「ブラウザベースアプリケーション向け OAuth 2.0」では、 ブラウザベースアプリケーションのセキュリティ特性がネイティブ アプリケーションのものとは大きく異なることを明らかにし、OAuth クライアントをネイティブ アプリケーションとして実装する場合とブラウザベースアプリケーションとして実装する場合の類似点を扱います。この文書は、 OpenID Connect によって追加の考慮事項が生じる場合を除き、主に OAuth に焦点を当てています。¶
これらの推奨事項の多くは、OAuth 2.0 セキュリティの現行のベストプラクティス [RFC9700]に由来しており、ブラウザベースアプリケーションも これらの推奨事項に従うことが 期待されています。この文書では、[RFC9700]で示されたさまざまな推奨事項を拡張し、さらに制限します。¶
この文書におけるキーワード「MUST」、「MUST NOT」、 「REQUIRED」、「SHALL」、「SHALL NOT」、「SHOULD」、「SHOULD NOT」、「RECOMMENDED」、「NOT RECOMMENDED」、 「MAY」、および「OPTIONAL」は、 ここに示すようにすべて大文字で表記されている場合に限り、 BCP 14 [RFC2119] [RFC8174]に記述されているとおりに 解釈されなければなりません。¶
この仕様では、「アクセストークン」、「認可エンドポイント」、 「認可グラント」、「認可サーバー」、「クライアント」、「クライアント識別子」(クライアント ID)、「保護された リソース」、「リフレッシュトークン」、「リソースサーバー」、および「トークンエンドポイント」という用語を、OAuth 2.0 [RFC6749]による定義に従って使用します。また、「ベアラートークン」を [RFC6750]による定義に従って使用します。¶
参照先の仕様で定義されている用語に加えて、この文書では 次の用語を使用します。¶
Web ブラウザに動的にダウンロードされて実行されるアプリケーションで、 通常は JavaScript で記述されます。「シングルページアプリケーション」または 「SPA」と呼ばれることもあります。¶
この文書では、ブラウザによってランタイム環境内で実行される ブラウザベースアプリケーションのセキュリティについて説明します。ほとんどのシナリオでは、これらは JavaScript 実行環境で動作する JavaScript アプリケーションです。このシナリオが広く利用されていることから、この 文書では「JavaScript」という用語を、ブラウザ内のアプリケーションの ランタイムでコードを実行できるようにするあらゆる仕組みを指すものとして使用します。この文書の推奨事項および考慮事項は、 JavaScript 言語やそのランタイムだけに関連するものではなく、 WebAssembly (WASM) [W3C.wasm-core-2]など、ブラウザ内のほかの言語やランタイム環境にも適用されます。¶
OAuth 2.0 が最初に [RFC6749]および [RFC6750]で規定された当時、ブラウザベースの JavaScript アプリケーションには、 同一オリジンポリシーに厳密に準拠する解決策が必要でした。OAuth 2.0 の一般的な展開では、 アプリケーションが認可サーバーとは異なるドメインで動作していたため、クロスオリジン POST リクエストを必要とする認可コードグラントタイプ (セクション 4.1([RFC6749])) を使用することは、歴史的に 不可能でした。この制限は、インプリシットグラント (セクション 4.2([RFC6749])) が定義された動機の一つです。インプリシットグラントは、 URL のフラグメント部分を介してフロントチャネルでアクセストークンを返すことで、クロスオリジン POST リクエストを不要にします。¶
しかし、インプリシットグラントにはいくつかの欠点があり、その多くは URL 内でアクセストークンが露出することに関連する脆弱性です。これらの攻撃の分析と、 ブラウザでインプリシットグラントを使用することの欠点については、セクション 7.2を参照してください。その他の攻撃およびセキュリティ上の 考慮事項については、[RFC9700]を参照してください。¶
現代の Web 開発では、オリジン間リソース共有 (CORS) が広く採用されたことで、 [Fetch] (同一オリジン ポリシーに例外を設けられるようにする仕組み)、ブラウザベースアプリケーションが OAuth 2.0 認可コードフローを使用し、 トークンエンドポイントで認可コードをアクセストークンと交換するための POST リクエストを行えるようになりました。 認可コードグラントタイプではリフレッシュトークンを使用できるため、この動作は、 安全なストレージにほとんど、またはまったくアクセスできないパブリッククライアント (セクション 2.1([RFC6749])で定義) であるブラウザベースクライアントにも 採用されています。さらに、コード交換用証明キー (PKCE) [RFC7636]をフローに追加すると、認可コードインジェクションを防止できるだけでなく、 認可コードが傍受された場合でも、攻撃者がそれを使用できないことが保証されます。¶
この理由と、そのほかに得られた知見から、ブラウザベースアプリケーションにおける現在のベストプラクティスは、 PKCE を使用した OAuth 2.0 認可コードグラントタイプを使用することです。ブラウザベースアプリケーションの展開には、 対応するサーバー側コンポーネントを使用するものと使用しないものを含め、 さまざまなアーキテクチャパターンがあります。各アーキテクチャには固有のトレードオフと 考慮事項があり、この文書で詳しく説明します。 ファーストパーティーの共通ドメインアプリケーションには、追加の考慮事項が適用されます。¶
悪意のある JavaScript は、ブラウザベースアプリケーションに重大なリスクをもたらします。クロスサイトスクリプティング (XSS) やリモートコードファイルの侵害などの攻撃ベクトルにより、攻撃者は アプリケーションの実行コンテキスト内で任意のコードを実行できるようになります。この悪意のあるコードは、 メインアプリケーションのコードから何ら分離されていません。その結果、悪意のあるコードは 実行中の実行コンテキストを制御できるだけでなく、アプリケーションのオリジン内で操作を実行することもできます。 具体的には、悪意のあるコードは現在のページからデータを盗み、ほかの 同一オリジンの閲覧コンテキストとやり取りし、アプリケーションのオリジン内からバックエンドへリクエストを送信し、 オリジンベースのストレージ機構 (例: localStorage、IndexedDB) からデータを盗むなどの操作を実行できます。¶
何よりもまず、攻撃者が足掛かりを得ること自体を避けるため、事前対策を講じることが重要です。 これには、次の対策が含まれますが、これらに限定されません。¶
信頼できないデータを処理するときに、コンテキストに応じた出力エンコーディングとサニタイズを 厳格に適用する¶
検証されていないサードパーティーリソースの読み込みを制限または回避する¶
nonce ベースまたはハッシュベースのコンテンツセキュリティポリシー (CSP) [W3C.CSP3]を使用して、認可されていない スクリプトコードの実行を防止する¶
オリジン分離および HTML5 サンドボックスを使用して、アプリケーションの 異なる部分間に境界を設ける¶
その他の推奨事項については、Open Worldwide Application Security Project (OWASP) チートシートシリーズ [OWASPCheatSheet]を参照してください。¶
残念ながら、これらのセキュリティガイドラインを適用した場合でも、 攻撃者が悪意のある JavaScript の実行を引き起こす方法を見つける可能性があることを、これまでの事例が示しています。 悪意のある JavaScript が存在することを前提としてブラウザベースアプリケーションのセキュリティを分析する際には、 悪意のある JavaScript コードは 正規のアプリケーションコードと同じ権限を持つことを認識することが極めて重要です。すべての JavaScript アプリケーションは、ある 程度このリスクにさらされています。¶
アプリケーションは、その機能に必要な認可を付与する OAuth トークンを 取得する場合があります。このため、侵害されたコードは実質的に、 その認可を悪意のある目的に使用できるようになります。 攻撃者による認可の悪用リスクを回避することはできませんが、侵害されたアプリケーションが その認可を悪用できる範囲を制限する方法はあります。 たとえば、取得可能なトークンの種類を制限するか、トークンを ブラウザにバインドすることで、アプリケーションが実際に使用されている時間だけにアクセスを 制限できます。¶
正規のアプリケーションコードが変数にアクセスしたり関数を呼び出したりできる場合、 悪意のある JavaScript コードもまったく同じことができます。さらに、悪意のある JavaScript コードは、 アプリケーションの通常の実行フローだけでなく、アプリケーションレベルの防御も改ざんできます。 通常、これらはアプリケーション内から制御されているためです。たとえば、攻撃者はイベントリスナーを削除または 上書きし、組み込み関数の動作を変更し (プロトタイプ汚染)、フレーム内の ページの読み込みを停止できます。¶
悪意のある JavaScript がブラウザベースアプリケーションに与える影響は、広く研究され、 十分に理解されているトピックです。しかし、OAuth クライアントとして動作するブラウザベース アプリケーションに悪意のある JavaScript が与える具体的な影響は、OAuth フロー中の やり取りにも影響を及ぼせるため、非常に特異です。このセクションでは、OAuth クライアントとしての責務を持つ ブラウザベースアプリケーションに対して、悪意のある JavaScript コードがもたらす脅威を検討します。セクション 5.1では、攻撃者が悪意のある JavaScript コードを実行する方法を 見つけた後に利用できるいくつかのシナリオについて説明します。これらのシナリオは、 単純なトークンの外部流出をはるかに超える、攻撃者の真の能力を明確に示しています。セクション 5.2では、これらの攻撃シナリオが OAuth クライアントに与える影響を分析します。¶
この仕様の残りの部分では、さまざまなアーキテクチャパターンのセキュリティ特性を分析するため、 これらの攻撃シナリオとその影響を参照します。¶
このセクションでは、悪意のある JavaScript コードの実行を可能にする脆弱性を 見つけた後に、攻撃者が実行できるいくつかの攻撃シナリオを示します。 攻撃シナリオには、単純なシナリオ (セクション 5.1.1) と複雑なシナリオ (セクション 5.1.3) が含まれます。この 列挙は網羅的ではなく、OAuth 固有の機能だけを対象としており、 特定の順序で示されているものではないことに注意してください。¶
単一実行によるトークン窃取シナリオは、攻撃者がクライアントの現在の トークンを取得して外部へ持ち出す、単純なトークン外部流出攻撃を対象とします。 このシナリオは、次の手順で構成されます。¶
侵害されたアクセストークンに伴うリスクを低減するために推奨される 防御戦略は、トークンのスコープと有効期間を縮小することです。 リフレッシュトークンについては、リフレッシュトークンローテーション (セクション 4.14.2([RFC9700])で定義) を使用することで、 検出および修正の仕組みが提供されます。送信者制約付きトークン (セクション 9.2) は、 盗まれたアクセストークンに対する追加の保護層を提供します。¶
この攻撃シナリオは単純であり、悪意のある JavaScript の危険性を 説明するためによく使用されることに注意してください。ブラウザベースアプリケーションのセキュリティについて 議論する際には、攻撃者の能力をこのシナリオで説明した攻撃だけに 限定して考えないことが極めて重要です。¶
持続的なトークン窃取攻撃シナリオは、単一実行によるトークン窃取シナリオ (セクション 5.1.1) の、より高度な 変形です。悪意のあるコードの実行時に直ちに トークンを盗む代わりに、攻撃者はアプリケーションのトークンを継続的に盗むために必要な ハンドラーを設定します。このシナリオは、 次の手順で構成されます。¶
このシナリオにおける決定的な違いは、攻撃者が常に アプリケーションで使用されている最新のトークンへアクセスできることです。このわずかな 攻撃シナリオの変化だけで、有効期間の短縮や リフレッシュトークンローテーションなど、一般的なトークン窃取対策を回避できます。¶
アクセストークンの場合、攻撃者はユーザーのブラウザがオンラインである限り、 最新のアクセストークンを取得し続けます。リフレッシュトークンローテーションだけでは、 リフレッシュトークンの悪用を防止できません。攻撃者は、アプリケーションが 最新のリフレッシュトークンを使用しないようにすることが容易にできます。たとえば、攻撃者は アプリケーションのトークンを盗んだ後に消去するか、ユーザーがアプリケーションを閉じるまで待つか、 ユーザーのブラウザがオフラインになるまで待つことができます。アプリケーションが 最新のリフレッシュトークンを使用しないため、検出可能なリフレッシュトークンの再利用は発生せず、 攻撃者は盗んだリフレッシュトークンを完全に制御できます。¶
高度な新しいトークンの取得および抽出の攻撃 シナリオでは、攻撃者はアプリケーションがすでに取得しているトークンを完全に無視します。 代わりに、攻撃者はアプリケーションのオリジンに関連付けられた 悪意のあるコードを実行できることを利用します。この能力により、攻撃者は 非表示の iframe を挿入し、サイレント認可コードフローを開始できます。このサイレントフローは、 認可サーバーにあるユーザーの既存のセッションを再利用し、新しい独立したアクセストークン (および必要に応じてリフレッシュトークン) を発行します。このシナリオは、 次の手順で構成されます。¶
悪意のある JavaScript コードを実行する¶
iframe から認可コードを取得するためのハンドラーを 設定する (例: フレームの URL の監視、または Web Messaging [WebMessaging]の使用)¶
非表示の iframe をページに挿入し、 認可リクエストで初期化する。iframe 内の認可リクエストは、 ユーザーのセッション内で行われ、セッションがまだ有効であれば、 認可コードが発行される。この手順は、 このシナリオの最後の段落で説明するように、認可サーバーがフレームベースのサイレントフローを サポートしていることに依存する点に注意する¶
事前にインストールしたハンドラーを使用して iframe から 認可コードを抽出する¶
攻撃者が制御するサーバーへ認可コードを 送信する¶
認可コードを新しい一組の トークンと交換する¶
盗んだトークンを悪用する¶
このシナリオから得られる最も重要な点は、既存のトークンを盗むことに重点を置くのではなく、 新しい OAuth フローを実行することです。つまり、 保存されたトークンを攻撃者から完全に分離できる トークンストレージ機構をアプリケーションが見つけたとしても、攻撃者は新しい一組の トークンを要求できます。攻撃者はブラウザ内のアプリケーションを制御しているため、 攻撃者の認可コードフローは正規の認可 コードフローと区別できないことに注意してください。¶
この攻撃シナリオが可能なのは、パブリックな ブラウザベース OAuth クライアントのセキュリティが、リダイレクト URI とアプリケーションの オリジンに全面的に依存しているためです。攻撃者がアプリケーションの オリジンで悪意のある JavaScript コードを実行すると、同一オリジンのフレームを検査できるようになります。その結果、 メインの実行コンテキストで動作する攻撃者のコードは、同一オリジンのフレームに 読み込まれたリダイレクト URI を検査して、認可コードを抽出できます。¶
フロントエンドアプリケーションには、この攻撃シナリオに対抗する 実用的なセキュリティ機構がありません。攻撃者は新しい独立した一組のトークンを持つため、アクセストークンの 有効期間の短縮やリフレッシュトークン ローテーションは効果がありません。DPoP [RFC9449]などの高度なセキュリティ機構も同様に効果がありません。攻撃者は、 独自の鍵ペアを使用して、新しく取得した トークン用に DPoP を設定して使用できるためです。すべての認可コードフローでユーザー操作を要求すれば、 新しいトークンの自動的なサイレント発行を効果的に阻止できますが、 アプリケーションを最初のページ読み込み時に起動する処理や、関連する複数のアプリケーション間での シングルサインオンなど、広く確立されたパターンに大きな影響を与えるため、 実用的な対策ではありません。¶
この攻撃シナリオでは、攻撃者がユーザーのブラウザで動作する OAuth クライアントアプリケーション内から、OAuth リソースサーバーへ直接リクエストを送信します。 このシナリオでは、ブラウザが自身の Cookie またはトークンを リクエストに含めるため、攻撃者がアプリケーションを悪用してトークンを取得する必要はありません。 攻撃者がリソースサーバーへ送信するリクエストは、 攻撃者が正規のアプリケーションと同じコンテキストで コードを実行しているため、正規のアプリケーションが送信するリクエストと区別できません。このシナリオは、 次の手順で構成されます。¶
リソースサーバーへのリクエストを認可するため、攻撃者は クライアントアプリケーションの動作を模倣するだけです。たとえば、クライアント アプリケーションが送信リクエストにプログラムによってアクセストークンを付加する場合、攻撃者も 同じことを行います。クライアントアプリケーションが、適切なアクセストークンを リクエストに付加する外部コンポーネントに依存している場合、その外部コンポーネントは 攻撃者のリクエストにもトークンを付加します。¶
この攻撃パターンはよく知られており、HttpOnly セッション Cookie を使用するサーバー側
Web アプリケーションでも発生します。このシナリオは、アプリケーションレベルのセキュリティ
対策では阻止または防止できないと一般に認識されています。
たとえば、DPoP [RFC9449]では、この攻撃シナリオを明示的に
範囲外としています。¶
攻撃シナリオの実行に成功すると、アクセストークンや リフレッシュトークンが盗まれるか、ユーザーのブラウザで動作しているクライアントアプリケーションを ハイジャックできるようになる可能性があります。これらの影響はいずれもブラウザベース OAuth クライアントに関係します。以下では、 重大度の高い順に説明します。¶
攻撃者がブラウザベース OAuth クライアントから有効なリフレッシュトークンを取得すると、そのリフレッシュトークンを使用して 認可サーバーに対するリフレッシュトークングラントを実行し、悪用できます。 リフレッシュトークングラントのレスポンスにはアクセストークンが含まれ、 攻撃者は保護されたリソースへアクセスできるようになります (セクション 5.2.2を参照)。つまり、 盗まれたリフレッシュトークンを悪用すると、リソースサーバーへのリクエストで 正規のクライアントアプリケーションになりすますことが長期間可能になります。¶
この攻撃が停止するのは、リフレッシュトークンの期限切れまたはローテーションを理由に 認可サーバーがそのトークンを拒否した場合、あるいはリフレッシュトークンが失効させられた場合だけです。 一般的なブラウザベース OAuth クライアントでは、リフレッシュトークンが 数時間、場合によっては数日間有効であり続けることも珍しくありません。¶
攻撃者が有効なアクセストークンを取得すると、 リソースサーバーへのリクエストで正規のクライアントアプリケーションになりすますことができます。 具体的には、アクセストークンを所有している攻撃者は、その有効なアクセストークンを受け入れる 任意のリソースサーバーへ任意のリクエストを送信できます。つまり、盗まれた アクセストークンを悪用すると、リソースサーバーに対して正規のクライアントアプリケーションになりすますことが 短期間可能になります。¶
この攻撃は、アクセストークンの期限が切れるか、 認可サーバーでトークンが失効させられると終了します。一般的なブラウザベース OAuth クライアントでは、 アクセストークンの有効期間は数分から数時間程度と、かなり短い場合があります。¶
アクセストークンを所有すると、攻撃者はそのトークンを制限なく 使用できることに注意してください。攻撃者は、任意の HTTP メソッド、送信先 URL、 ヘッダー値、または本文を使用して、リソースサーバーへ任意のリクエストを送信できます。¶
アプリケーションは DPoP を使用して、アクセストークンをブラウザが保持する エクスポート不可能な鍵にバインドできます。この場合、 攻撃者が盗んだアクセストークンを悪用することは大幅に難しくなります。より具体的には、DPoP を使用すると、 攻撃者が盗んだアプリケーションのトークンを悪用できるのは、 ユーザーのブラウザで証明を計算するオンライン攻撃を実行する場合だけです。この攻撃については、 セクション 11.4([RFC9449])で詳しく説明されています。しかし、 セクション 5.1.3で 説明したように、攻撃者が新しいアクセストークン (および必要に応じてリフレッシュトークン) を取得すると、 攻撃者が制御する鍵ペアを使用して、これらのトークン用に DPoP を設定できます。その 場合、攻撃者は新しく取得したアクセストークンを再び制限なく 自由に悪用できます。¶
トークンを盗むことが不可能または望ましくない場合、攻撃者は ユーザーのブラウザで動作する OAuth クライアントアプリケーションをハイジャックすることもできます。これにより、 攻撃者は正規のクライアントアプリケーションが実行できるあらゆる操作を実質的に 実行できます。たとえば、ページ上のデータの検査、ページの変更、 バックエンドシステムへのリクエストの送信などです。あるいは、攻撃者は アプリケーションへのアクセスを悪用し、セッション固定攻撃などを使用して クライアントを攻撃者の代理として動作させるといった追加攻撃を開始することもできます。 [SessionFixation]。¶
クライアントハイジャックは、盗んだユーザートークンを直接悪用するよりも強力ではないことに
注意してください。クライアントハイジャックのシナリオでは、攻撃者はトークンを直接制御できず、
クライアントアプリケーションに適用されるセキュリティポリシーによって制限されます。
たとえば、admin.example.orgで動作するリソースサーバーは、
web.example.orgで動作するクライアントからのリクエストを拒否する
CORS ポリシーを設定できます。クライアントが使用するアクセストークンが
リソースサーバーに受け入れられる場合でも、そのリソースサーバーの厳格な CORS 設定によって
そのようなリクエストは許可されません。このような厳格な CORS ポリシーがないリソースサーバーは、
侵害されたクライアントアプリケーションから送信される敵対的なリクエストの影響を受ける可能性があります。¶
保護されたリソースへのアクセスに OAuth を利用するブラウザベース アプリケーションを構築する際には、主に次の3つのアーキテクチャパターンを使用できます。¶
OAuth に関する責務の処理をバックエンドコンポーネントに委ね、 すべてのリクエストをそのバックエンドコンポーネント経由で転送するブラウザベースアプリケーション (「フロントエンド向けバックエンド」または「BFF」)¶
OAuth に関する責務の処理をバックエンドコンポーネントに委ねる一方、 アクセストークンを使用してリソースサーバーを直接呼び出すブラウザベースアプリケーション (トークン仲介バックエンド)¶
クライアントとして動作し、すべての OAuth に関する責務を ブラウザ内で処理するブラウザベースアプリケーション (ブラウザベース OAuth クライアント)¶
これらの各アーキテクチャパターンは、セキュリティと 単純さの間で異なるトレードオフを提供します。このセクションでは、セキュリティが高い順にパターンを示します。¶
このセクションでは、すべての OAuth に関する責務と API のやり取りの処理を バックエンドコンポーネントに委ねるブラウザベースアプリケーションのアーキテクチャについて説明します。BFF には、 次の3つの中核的な責務があります。¶
BFF は、コンフィデンシャル OAuth クライアント (セクション 2.1([RFC6749])で定義) として認可サーバーとやり取りします。¶
BFF は、Cookie ベースのセッションのコンテキスト内で OAuth のアクセストークンと リフレッシュトークンを管理し、ブラウザベース アプリケーションにトークンが直接露出することを回避します。¶
BFF はすべてのリクエストをリソースサーバーへ転送し、 転送前に適切なアクセストークンをリクエストへ付加します。¶
このアーキテクチャでは、BFF はサーバー側コンポーネントとして動作しますが、 フロントエンドアプリケーションのコンポーネントです。API ゲートウェイやリバースプロキシなど、 ほかのアーキテクチャ上の概念との混同を避けるため、BFF が フロントエンドアプリケーションの OAuth クライアントになることを念頭に置くことが重要です。¶
攻撃者がブラウザベース
アプリケーション内で悪意のあるコードを実行できる場合でも、このアプリケーションアーキテクチャは、前述した
攻撃シナリオの大部分に耐えることができます。トークンは BFF でのみ利用可能であるため、
ブラウザから抽出できるトークンはありません (「単一実行によるトークン窃取」(セクション 5.1.1) および「持続的なトークン窃取」(セクション 5.1.2) を参照)。BFF は
コンフィデンシャルクライアントであるため、攻撃者がブラウザ内で新しいフローを実行することを防止します
(「新しいトークンの取得および抽出」(セクション 5.1.3) を参照)。悪意のあるブラウザベースのコードは依然として
アプリケーションのオリジン内で動作するため、攻撃者はユーザーのブラウザ内から
BFF へリクエストを送信できます (「ユーザーのブラウザを介したリクエストのプロキシ」(セクション 5.1.4) を参照)。
HttpOnly Cookie を使用すると、攻撃者がセッション状態へ直接アクセスできなくなり、
クライアントハイジャックからセッションハイジャックへの発展を防止できることに注意してください。¶
このアーキテクチャでは、まずブラウザコード (通常は JavaScript) が 静的 Web ホストからブラウザへ読み込まれ (A)、その後、アプリケーションがブラウザ内で動作します。 アプリケーションは「セッション確認」API エンドポイント (B) を呼び出し、 有効なセッションがあるかどうかを BFF に確認します。有効なセッションが見つかった場合、 アプリケーションは認証済み状態を再開し、(J) へ進みます。¶
有効なセッションが見つからない場合、ブラウザベースアプリケーションは、 PKCE 拡張機能を使用した認可コードフローを開始するため、BFF へのナビゲーション (C) を トリガーします (セクション 6.1.3.1で説明)。これに対して BFF は、ブラウザを 認可エンドポイント (D) へリダイレクトします。ユーザーがリダイレクトされて戻ると、ブラウザは 認可コードを BFF (E) へ渡します。BFF は、クライアントクレデンシャルと PKCE コードベリファイアを使用し、 トークンエンドポイント (F) で認可コードをトークンと交換できます。¶
BFF は取得したトークンをユーザーのセッションに関連付け (セクション 6.1.2.3を参照)、 このセッションを追跡するため、レスポンスに Cookie を設定します (G)。この時点で リダイレクトベースの認可コードフローは完了しているため、BFF は制御を フロントエンドアプリケーションへ戻せます。BFF は、レスポンスにリダイレクトを含めることで これを行い (G)、ブラウザにサーバーからフロントエンドを取得させます (H)。手順 (H) は手順 (A) と同一であるため、要求されたリソースは ブラウザのキャッシュから読み込まれる可能性が高いことに注意してください。フロントエンドが読み込まれると、既存の セッションについて BFF に確認し (I)、アプリケーションが認証済み状態を再開できるようにします。¶
ブラウザ内のアプリケーションがリソースサーバーへリクエストを送信するとき、 BFF 上の対応するエンドポイントへリクエストを送信します (J)。この リクエストには (G) で設定された Cookie が含まれるため、BFF はこのユーザーのセッションに対応する 適切なトークンを取得できます。BFF はリクエストから Cookie を削除し、 ユーザーのアクセストークンをリクエストに付加して、実際のリソースサーバーへ転送します (K)。 その後、BFF はレスポンスをブラウザベースアプリケーションへ転送します (L)。¶
BFF は、ブラウザベースアプリケーションと BFF の間の やり取りを実装するうえで重要な一連のエンドポイントを提供します。 このセクションでは、その目的と ユースケースを明確にするため、これらのエンドポイントについて詳しく説明します。¶
「セッション確認」エンドポイント (図1の (B) および (I)) は、 ブラウザベースアプリケーションが呼び出す API エンドポイントです。利用可能な場合、リクエストには セッション情報が含まれ、BFF が有効な セッションを確認できるようになります。レスポンスは、セッションが有効かどうかを ブラウザベースアプリケーションに示す必要があります。さらに BFF は、 認証済みユーザーの識別情報など、ほかの情報を含めることができます。¶
認可コードフローを開始するエンドポイント (C) には、ブラウザのナビゲーションによってアクセスします。アプリケーションは、 セッション確認後 (B) に未認証状態を検出した場合、 ブラウザをこのエンドポイントへ移動させることができます。これにより、BFF は ブラウザを認可サーバーへ移動させるリダイレクトで応答できます。通常、このフローを 開始するエンドポイントは、Web サーバー上で動作するコンフィデンシャルクライアント向けに OAuth 2.0 をサポートするライブラリによって「ログイン」エンドポイントとして提供されます。 BFF が (B) で有効なセッションが存在しないことを検出した際に、 認可コードフローを開始することも可能です。その場合、BFF は レスポンスで認可 URI を返し、アプリケーションが この URI を使用してナビゲーションイベントをトリガーすることを想定します。ただし、このシナリオには カスタム実装が必要となり、標準の OAuth ライブラリを使用しにくくなります。¶
認可コードを受け取るエンドポイント (E) は、 ブラウザ内からのナビゲーションイベントによって呼び出されます。この時点では アプリケーションは読み込まれておらず、リダイレクトを処理できる状態ではありません。 フローの開始時と同様に、リダイレクトを処理するエンドポイントは 標準の OAuth ライブラリによって提供されます。BFF は、このリクエストに対し、 ブラウザにアプリケーションを読み込ませるリダイレクトで応答できます。¶
最後に、BFF はアプリケーションに 「ログアウト」エンドポイントを提供することもできます。これは 図1には示されていません。ログアウト エンドポイントの正確な動作は、アプリケーションの要件によって異なります。標準の OAuth ライブラリでも通常、「ログアウト」エンドポイントの実装が提供されることに注意してください。¶
セクション 4.14([RFC9700])で説明されているようにリフレッシュトークンを使用する場合、BFF は リフレッシュトークン (F) を取得し、ユーザーのセッションに関連付けます。¶
ユーザーのアクセストークンが 期限切れになったことを BFF が検出し、BFF がリフレッシュトークンを保持している場合、 リフレッシュトークンを使用して新しいアクセストークンを取得できます。BFF の OAuth クライアントは コンフィデンシャルクライアントであるため、リフレッシュトークンリクエストでクライアント認証を使用します。通常、BFF は、 フロントエンドからの API 呼び出しを処理する際に、これらの手順をインラインで実行します。その 場合、図1には明示されていないこれらの手順が、 (J) と (K) の間で実行されます。すべてのトークン情報を サーバー側で利用可能な状態に保つ BFF は、トークンの期限切れイベントを検出したときに 新しいアクセストークンを要求し、API リクエストのパフォーマンスを向上させることもできます。¶
リフレッシュトークンの期限が切れると、まったく新しい認可コードフローを 実行しない限り、有効なアクセストークンを取得する方法はありません。 したがって、BFF が管理する Cookie ベースのセッションの有効期間を、 リフレッシュトークンの最大有効期間と等しく設定することは合理的です。 さらに、有効なセッションのリフレッシュトークンが 無効になったことを BFF が認識した場合、そのセッションも無効にすることが合理的です。¶
BFF は、ユーザーのトークンへのアクセスに使用される セッションを追跡するため、ブラウザの Cookie [RFC6265bis]を利用します。サーバー側と クライアント側のどちらの Cookie ベースセッションにも、いくつかの欠点があります。¶
サーバー側セッションでは、セッション識別子だけを公開し、 すべてのデータをサーバーに保持します。これにより、有効なセッションを高度に制御でき、 任意のセッションを必要に応じて失効させることもできます。この 手法の欠点はスケーラビリティへの影響であり、「スティッキーセッション」や 「セッションレプリケーション」などの解決策が必要になります。これらの欠点を考慮すると、 BFF でのサーバー側セッションの使用は、小規模なシナリオでのみ推奨されます。¶
クライアント側セッションでは、すべてのデータを署名付き、 必要に応じて暗号化されたオブジェクトとしてブラウザへ送信します。このパターンにより、サーバーは セッションデータを追跡する必要がなくなりますが、有効な セッションの制御が大幅に制限され、セッションの失効処理が困難になります。ただし、 クライアント側セッションを BFF のコンテキストで使用する場合、これらの特性は 大きく変化します。Cookie ベースのセッションはユーザーの トークンを取得するためだけに使用されるため、すべての制御および失効の特性は、アクセストークンと リフレッシュトークンの使用に基づきます。Cookie ベースのセッションを明示的に 無効にしなくても、ユーザーのアクセストークンや リフレッシュトークンを失効させるだけで、保護されたリソースへの継続的なアクセスを防止できます。¶
セッション Cookie を保護するためのベストプラクティスについては、 セクション 6.1.3.2で説明します。¶
このアプリケーションアーキテクチャで使用する OAuth フローは、
認可リクエスト (C) に必要な OpenID Connect スコープ
(少なくとも [OpenID]のセクション 3.1.2.1 で定義されている
openid スコープ) を含めることで、OpenID Connect と組み合わせることができます。
この場合、BFF は (F) で ID トークンを受け取ります。BFF は ID
トークンの情報をユーザーのセッションに関連付け、(B) または (I) でアプリケーションに提供できます。¶
必要な場合、BFF はユーザーのセッションに関連付けられた アクセストークンを使用して、UserInfo エンドポイントへリクエストを送信できます。¶
静的 JavaScript コードの配信は、 OAuth トークンの処理およびリクエストの転送とは別の責務です。図1では、 BFF と静的 Web ホストが2つの別個の主体として示されています。実際の 展開では、これらのコンポーネントを単一のサービス (すなわち、BFF が 静的 JavaScript コードを配信)、2つの別個のサービス (すなわち、コンテンツ 配信ネットワーク (CDN) と BFF)、または単一サービス内の2つのコンポーネント (すなわち、クラウドプラットフォーム上の静的ホスティングとサーバーレス関数) として展開できます。¶
特定のシナリオをサポートするため、この アーキテクチャをさらにカスタマイズできることに注意してください。たとえば、内部と外部の 両方のリソースサーバーを利用するアプリケーションは、内部 リソースサーバーを BFF と共にホストできます。このシナリオでは、内部 リソースサーバーへのリクエストは、ネットワーク経由で 転送することなく、BFF で直接処理されます。ユーザーのセッションは内部で アクセストークンおよびそのクレームへ変換されるため、リソースサーバーから見た認可は 変わりません。¶
BFF を使用する主な利点は、BFF が コンフィデンシャルクライアントとして動作できることです。したがって、BFF は MUST、 認可サーバーとの間でクレデンシャルを確立し、コンフィデンシャルクライアントとして 動作しなければなりません。さらに、BFF は MUST、 アクセストークンのリクエストを開始するため、セクション 2.1.1([RFC9700])で説明されている OAuth 2.0 認可コードグラントを 使用しなければなりません。¶
ブラウザベースアプリケーションと BFF の間のやり取りでは、認証と認可に Cookie を使用します。ほかの Cookie ベースのやり取りと同様に、BFF はクロスサイトリクエスト フォージェリ (CSRF) 攻撃を考慮する必要があります。CSRF 攻撃が成功すると、 BFF への攻撃者のリクエストによって、保護されたリソースへの外向きの呼び出しがトリガーされる可能性があります。¶
BFF は MUST、適切な CSRF 防御を実装しなければなりません。使用する具体的な仕組みまたは仕組みの組み合わせは、 以下で説明するように、BFF が展開される ドメインによって異なります。¶
BFF は、CSRF 防御 機構として CORS を利用できます。CORS はブラウザに実装されたセキュリティ機構で、 サーバーが適切な CORS ヘッダーを設定して リクエストを明示的に承認しない限り、クロスオリジンリクエストを制限します。¶
通常、ブラウザはスクリプトから開始されたクロスオリジン HTTP リクエストを制限します。対象 サーバーがリクエストを承認した場合、CORS はこの制限を解除できます。この承認は、最初の 「プリフライト」リクエストによって確認されます。プリフライトレスポンスで リクエストが明示的に承認されない限り、ブラウザは完全なリクエストの送信を拒否します。¶
この特性により、BFF は CSRF 防御として CORS を利用できます。攻撃者がユーザーのブラウザから BFF へ クロスオリジンリクエストを送信しようとすると、BFF は プリフライトレスポンスでリクエストを承認しないため、ブラウザは 実際のリクエストをブロックします。攻撃者は自身のコンピューターから リクエストを送信できますが、その場合、リクエストにはユーザーの Cookie が含まれないため、攻撃は失敗します。¶
CSRF 防御として CORS を利用する場合、 特定のリクエストは プリフライトなしで送信できることを認識することが重要です。「CORS セーフリストリクエスト」と呼ばれるこのようなリクエストでは、 ブラウザは単にリクエストを送信し、サーバーが適切な CORS ヘッダーを送信しなかった場合、 レスポンスへのアクセスを防止します。この動作は、 GET リクエストやフォームベースの POST リクエストなど、 JavaScript 以外の手段によってトリガーできるリクエストに適用されます。¶
この動作の結果、防御として CORS が有効になっている場合でも、リソースサーバーの特定の エンドポイントが CSRF に対して脆弱になる可能性があります。たとえば、リソースサーバーが 本文のない POST リクエストを受け付けるエンドポイントを公開する API である場合、 プリフライトリクエストも CSRF 防御も行われません。¶
CORS ポリシーに対するこのような迂回を防ぐため、
BFF は SHOULD、ブラウザベース
アプリケーションにカスタムリクエストヘッダーを含めるよう要求すべきです。
カスタムリクエストヘッダーを含むクロスオリジンリクエストでは、常にプリフライトが必要となるため、
CORS が効果的な CSRF 防御になります。この機構を使用する場合、BFF は MUST、すべての受信リクエストに
この静的ヘッダーが含まれていることを確認しなければなりません。このヘッダーの具体的な名前は、
アプリケーションと BFF の裁量で決定できます。設定例として、
My-Static-Header: 1のような静的な値を持つリクエスト
ヘッダーを使用できます。¶
ブラウザベース アプリケーションを BFF と同じオリジンに展開することもできます。これにより、 フロントエンドと BFF の正規のやり取りで プリフライトが不要となり、追加のオーバーヘッドが発生しません。¶
BFF パターンでは、ブラウザベース アプリケーションがリソースサーバーへのすべてのリクエストを、バックエンドの BFF コンポーネント経由で転送する必要があります。その結果、BFF コンポーネントは、 アプリケーションとリソースサーバー間のすべてのリクエストとレスポンスを監視でき、 プライバシーに大きな影響を与える可能性があります。¶
ブラウザベースアプリケーションと BFF が同じ主体によって 構築および展開される場合、プライバシーへの影響は最小限である可能性があります。ただし、 サードパーティーによって提供またはホストされる BFF コンポーネントを使用してこのパターンを実装する場合、このプライバシーへの影響を考慮する必要があります。¶
BFF はフロントエンドに代わって すべてのリクエストをリソースサーバーへ転送するため、リソース サーバーがこのコンポーネントを認識し、レート 制限やその他の不正使用防止対策に適切なポリシーを使用するよう注意する必要があります。たとえば、 BFF が単一インスタンスのサービスとして展開され、リソースサーバーが IP アドレスに基づいてリクエストをレート制限している場合、多数のユーザーの ブラウザが BFF の単一の IP アドレスからアクセスしているように見えるため、 サーバーがリクエストをブロックし始める可能性があります。¶
BFF は、フロントエンドからリクエストを受け取り、 リソースサーバーへ転送することで、プロキシサービスとして動作します。受信リクエストには Cookie が含まれ、BFF は送信リクエストでそれをアクセストークンへ変換します。 (このため、BFF は HTTP プロキシよりもアプリケーション層のリバースプロキシに近いものとなります。) CSRF 攻撃のほかに、攻撃者は BFF を操作して、 意図しないホストへリクエストを転送させようとする可能性があります。攻撃者が これを悪用することに成功すると、BFF を任意のサーバーへリダイレクトし、 ユーザーのアクセストークンを露出させる可能性があります。¶
このリスクを緩和するため、BFF は MUST、リクエストを転送する前に送信先ホストを
検証し、厳格な送信リクエスト制御を適用しなければなりません。これには、
承認されたリソースサーバーの明示的な許可リストを維持し、
リクエストが事前定義されたバックエンドにのみプロキシされることを保証する必要があります (例:
/bff/orders/create は
https://order-api.example.com/createにのみマッピングされる)。
パスに基づく動的ルーティング (例: /bff/orders/{id}) が必要な場合、BFF は MUST、認可された送信先だけに
アクセスできるよう厳格な検証を適用しなければなりません。さらに、エンドポイントごとに許可する
HTTP メソッドを制限することで、攻撃ベクトルをさらに減らすことができます。¶
動的に設定可能なプロキシを実装する場合、 BFF は MUST、明示的に許可された ホストとパスへのリクエストだけを許可しなければなりません。これらの制限を適用しないと、 認可されていないアクセスやアクセストークンの漏洩につながる可能性があります。¶
BFF パターンでは、OAuth に関するすべての責務が、 コンフィデンシャルクライアントとして動作するサーバー側コンポーネントである BFF へ 移されます。サーバー側アプリケーションはブラウザベース アプリケーションよりも制御された環境で動作するため、高度な OAuth セキュリティプラクティスを 導入しやすくなります。たとえば、鍵ベースのクライアント認証や送信者制約付き トークンがあります。¶
このセクションでは、セクション 5の攻撃シナリオとその影響を再検討し、 追加できる可能性のある防御策について説明します。¶
攻撃者がアプリケーションの実行コンテキスト内で 悪意のあるコード (例: JavaScript または WASM) を実行できる場合、 次の攻撃シナリオが該当します。¶
ユーザーのブラウザを介したリクエストのプロキシ (セクション 5.1.4)¶
この攻撃シナリオは、次の 影響をもたらすことに注意してください。¶
クライアントハイジャック (セクション 5.2.3)¶
クライアントハイジャックは、 ブラウザベースアプリケーションの性質に内在する攻撃シナリオであることに注意してください。そのため、 悪意のあるコードの実行自体を阻止する以外に、 このような攻撃を防止する方法はありません。これに役立つ手法には、 セキュアコーディングガイドラインへの準拠、コード分析、コンテンツセキュリティポリシー [W3C.CSP3]などの多層防御 機構の展開があります。¶
このアーキテクチャでは、BFF は、 セキュリティ固有のさまざまな責務とプロキシベースの動作を処理する主要コンポーネントです。 プロキシベースアプリケーションの安全な実装について説明することは この文書の範囲外ですが、BFF のセキュリティ脆弱性が アプリケーションに重大な影響を与える可能性があることに注意することが極めて重要です。¶
最後に、BFF はブラウザベースアプリケーションと リソースサーバー間のすべてのトラフィックを監視できる独自の位置にあります。 高度なセキュリティを必要とするアプリケーションが異常検出やレート 制限を実装する場合、BFF はそのための理想的な場所です。このような制限は、 クライアントハイジャックによる影響をさらに緩和するのに役立ちます。¶
以下に示すほかの攻撃シナリオは、BFF アプリケーションアーキテクチャによって効果的に緩和されます。¶
単一実行によるトークン窃取 (セクション 5.1.1)¶
持続的なトークン窃取 (セクション 5.1.2)¶
新しいトークンの取得および抽出 (セクション 5.1.3)¶
BFF はブラウザベースアプリケーションに トークンを一切露出させないことで、最初の2つの攻撃シナリオに対抗します。攻撃者が アプリケーションを完全に制御した場合でも、盗めるトークンは 存在しません。¶
3番目のシナリオでは、攻撃者がサイレントフローを実行して 新しいアクセストークン (および必要に応じてリフレッシュトークン) を取得しますが、 BFF をコンフィデンシャルクライアントにすることで緩和されます。攻撃者が 認可コードの取得に成功した場合でも、クライアントクレデンシャルを持たないため、 そのコードを交換できません。さらに、PKCE を使用すると、 認可コードに対するほかの攻撃を防止できます。¶
リフレッシュトークンとアクセストークンは BFF によって管理され、 ブラウザに露出しないため、潜在的な攻撃がもたらす次の2つの影響は 該当しなくなります。¶
盗まれたリフレッシュトークンの悪用 (セクション 5.2.1)¶
盗まれたアクセストークンの悪用 (セクション 5.2.2)¶
BFF のアーキテクチャは、ブラウザのみのアプリケーションよりも 大幅に複雑です。サーバー側の BFF コンポーネントを展開して運用する必要があります。 さらに、このパターンでは、アプリケーションとリソースサーバー間のすべての やり取りを BFF がプロキシする必要があります。展開パターンによっては、 このプロキシ動作がサーバー側コンポーネントに 大きな負荷を加える可能性があります。BFF がリソース サーバーとして動作する場合の追加の注記については、セクション 6.2.2.6を参照してください。¶
ただし、BFF アーキテクチャ パターンの性質上、強力なセキュリティ保証が提供されます。BFF を使用すると、 OAuth の使用によってアプリケーションの攻撃対象領域が増加しないことも保証されます。実行可能な唯一の 攻撃パターンは、ユーザーのブラウザ内にあるクライアントアプリケーションのハイジャックです。これは、 Web アプリケーションに内在する問題です。¶
このアーキテクチャは、業務 アプリケーション、機密性の高いアプリケーション、および個人 データを扱うアプリケーションに強く推奨されます。¶
このセクションでは、コンフィデンシャルクライアントとしてトークンを取得するための OAuth に関する責務の処理をバックエンドコンポーネントに委ねる ブラウザベースアプリケーションのアーキテクチャについて説明します (セクション 2.1([RFC6749])で定義)。その後、バックエンドコンポーネントは、 リソースサーバーと直接やり取りするためのアクセストークンをアプリケーションに提供します。¶
トークン仲介バックエンドパターンは、アプリケーションとリソース サーバー間のすべてのリクエストとレスポンスをプロキシする必要がないため、BFF パターン (セクション 6.1を参照) より軽量です。 セキュリティの観点では、トークン仲介バックエンドは BFF よりも安全性が低いものの、 ブラウザ内で直接動作する OAuth クライアントアプリケーションと比べれば、依然として大きな 利点があります。¶
攻撃者がアプリケーション内で悪意のあるコードを実行できる場合でも、
このアプリケーションアーキテクチャは、攻撃者によるリフレッシュトークンの悪用
(「単一実行によるトークン窃取」(セクション 5.1.1) および「持続的なトークン窃取」(セクション 5.1.2) を参照) や、
新しい一組のトークンの取得 (「新しいトークンの取得および抽出」(セクション 5.1.3) を参照) を防止できます。ただし、
アクセストークンはアプリケーションに直接露出するため、攻撃者は
クライアント側ストレージからトークンを盗む
(「単一実行によるトークン窃取」(セクション 5.1.1) および「持続的なトークン窃取」(セクション 5.1.2) を参照) か、
トークン仲介バックエンドへ新しいトークンを要求できます
(「ユーザーのブラウザを介したリクエストのプロキシ」
(セクション 5.1.4) を参照)。
HttpOnly Cookie を使用すると、攻撃者がセッション状態へ直接アクセスできなくなり、
アクセストークン窃取からセッションハイジャックへの発展を防止できることに注意してください。¶
このアーキテクチャでは、まずブラウザベースのコード (例: JavaScript または WASM) が静的 Web ホストからブラウザへ読み込まれ (A)、 その後、アプリケーションがブラウザ内で動作します。アプリケーションは、 有効なセッションがあるかどうかをトークン仲介バックエンドに確認します (B)。 有効なセッションが見つかった場合、アプリケーションは対応するアクセストークンを受け取り、 認証済み状態を再開して (J) へ進みます。¶
有効なセッションが見つからない場合、アプリケーションは、 PKCE 拡張機能を使用した認可コードフローを開始するため、トークン仲介バックエンドへの ナビゲーション (C) をトリガーします (セクション 6.2.3.1で説明)。これに対してトークン仲介 バックエンドは、ブラウザを認可エンドポイント (D) へリダイレクトします。 ユーザーがリダイレクトされて戻ると、ブラウザは認可コードを トークン仲介バックエンド (E) へ渡します。トークン仲介バックエンドは、 クライアントクレデンシャルと PKCE コードベリファイアを使用し、トークンエンドポイント (F) で 認可コードをトークンと交換できます。¶
トークン仲介バックエンドは、取得したトークンを ユーザーのセッションに関連付け (セクション 6.2.2.4を参照)、このセッションを追跡するため、レスポンスに Cookie を設定します (G)。 ブラウザへのこのレスポンスは、アプリケーションの再読み込みもトリガーします (H)。 アプリケーションが再読み込みされると、既存の セッションについてトークン仲介バックエンドに確認し (I)、アプリケーションが認証済み状態を再開して トークン仲介バックエンドからアクセストークンを取得できるようにします。¶
ブラウザ内のアプリケーションは、(I) で取得したアクセストークンを使用して リソースサーバーへ直接リクエストを送信できます (J)。¶
トークン仲介バックエンドの エンドポイント実装の大部分は、BFF について説明したものと同様です。¶
「セッション確認」エンドポイント (図 2の (B) および (I)) は、ブラウザベースアプリケーションが呼び出す API エンドポイントです。利用可能な場合、リクエストにはセッション情報が含まれ、 バックエンドが有効なセッションを確認できるようになります。レスポンスは、 セッションが有効かどうかをブラウザベースアプリケーションに示す必要があります。 有効なセッションが見つかった場合、バックエンドはアクセストークンを レスポンスに含めます。さらに、バックエンドは、認証済みユーザーの 識別情報など、ほかの情報を含めることができます。¶
認可コード フローを開始するエンドポイント (C) は、BFF アーキテクチャについて説明したエンドポイントと同一です。詳細については、セクション 6.1.2.1を参照してください。¶
認可 コードを受け取るエンドポイント (E) は、BFF アーキテクチャについて説明したエンドポイントと同一です。詳細については、セクション 6.1.2.1を参照してください。¶
ログアウトをサポートするエンドポイントは、 BFF アーキテクチャについて説明したエンドポイントと同一です。詳細については、セクション 6.1.2.1を 参照してください。¶
セクション 4.14([RFC9700])で説明されているようにリフレッシュトークンを使用する場合、トークン仲介バックエンドは (F) でリフレッシュトークンを取得し、ユーザーのセッションに関連付けます。¶
リソースサーバーがアクセストークンを拒否した場合、 アプリケーションはトークン仲介バックエンドへ新しいアクセス トークンを要求できます。トークン仲介バックエンドは、この リクエストに関連付けられた Cookie を使用してユーザーのリフレッシュトークンを検索し、 リフレッシュトークンを使用してトークンリクエストを行います。これらの手順は、図2には示されていません。このリフレッシュトークン リクエストはコンフィデンシャルクライアントであるバックエンドから送信されるため、 クライアント認証が必要になることに注意してください。¶
リフレッシュトークンの期限が切れると、まったく新しい認可コードグラントを 開始しない限り、有効なアクセストークンを取得する方法はありません。 したがって、この情報が事前に判明している場合は、Cookie ベースのセッションの有効期間を リフレッシュトークンの最大有効期間と等しく設定することが合理的です。 さらに、有効なセッションのリフレッシュトークンが 無効になったことをトークン仲介バックエンドが認識した場合、そのセッションを 無効にすることが合理的です。¶
アクセスするリソースサーバーおよび 認可サーバーでのスコープ設定によっては、アプリケーションは、 異なるスコープ設定を持つアクセストークンを要求する場合があります。この動作により、 アプリケーションは、スコープを最小限に抑えた アクセストークンを使用するというベストプラクティスに従うことができます。¶
アプリケーションは、有効なセッションを確認する際 ((A) および (I)) に、必要なスコープをトークン仲介バックエンドへ通知できます。 以前に取得したアクセストークンが 必要なスコープの条件を満たすかどうかは、トークン仲介バックエンドが判断します。¶
トークン仲介バックエンドにおけるこのアクセストークンのキャッシュ 機構は、無差別に適用するとスコープ昇格のリスクを引き起こす可能性があることに 注意する必要があります。キャッシュされたアクセストークンが、 フロントエンドから要求されたスコープの上位集合を持つ場合、トークン仲介バックエンドは SHOULD NOT、そのトークンをフロントエンドへ返すべきではありません。代わりに、SHOULD、リフレッシュトークンを使用して、より小さなスコープ集合を持つ アクセストークンを認可サーバーへ要求すべきです。このような アクセストークンのスコープ縮小機構をサポートするかどうかは、 認可サーバーの裁量で決定されることに注意してください。¶
トークン仲介バックエンドは、 [RFC8707]で説明されている拡張機能を利用して 特定のリソースサーバー向けのアクセストークンを取得する場合にも、 スコープ縮小機構と同様の仕組みを使用できます。¶
BFF と同様に、トークン仲介バックエンドは、 ユーザーのセッションを追跡するためにブラウザの Cookie を利用します。Cookie ベースセッションの 実装ガイドラインとセキュリティ上の考慮事項は、セクション 6.1.2.3で説明した BFF のものと同じです。¶
BFF と同様に、トークン仲介バックエンドは、 OAuth と OpenID Connect を単一のフローに組み合わせることができます。詳細については、セクション 6.1.2.4を 参照してください。¶
静的 JavaScript または WASM コードのホスティングは、 認可サーバーとのやり取りの処理とは別の責務です。図2では、 トークン仲介バックエンドと静的 Web ホストが2つの別個の主体として示されています。 実際の展開シナリオでは、これらのコンポーネントを単一の サービス (すなわち、トークン仲介バックエンドが静的コードを配信)、2つの 別個のサービス (すなわち、CDN とトークン仲介バックエンド)、または単一サービス内の2つの コンポーネント (すなわち、クラウドプラットフォーム上の静的ホスティングとサーバーレス関数) として展開できます。これらの展開方法の違いは、このパターンで説明した関係には影響しませんが、 クロスオリジン通信を有効にするために CORS を適切に設定する必要性など、 ほかの実務上の事項に影響する可能性があります。¶
トークン仲介バックエンドを使用する主な利点は、 バックエンドがコンフィデンシャルクライアントとして動作できることです。したがって、 トークン仲介バックエンドは MUST、コンフィデンシャル クライアントとして動作しなければなりません。さらに、トークン仲介バックエンドは MUST、 アクセストークンのリクエストを開始するため、セクション 2.1.1([RFC9700])で説明されている OAuth 2.0 認可コードグラントを 使用しなければなりません。¶
ブラウザベースアプリケーションと トークン仲介バックエンドの間のやり取りでは、認証と認可に Cookie を使用します。 BFF と同様に、トークン仲介バックエンドは CSRF 攻撃を考慮する必要があります。¶
セクション 6.1.3.3では、 CSRF 攻撃に対するさまざまな緩和戦略の詳細を説明しています。 トークン仲介バックエンドの場合、これらの CSRF 防御は、 アプリケーションがアクセストークンを取得できるエンドポイントにのみ適用されます。¶
トークン仲介バックエンドは、 サーバー側コンポーネントとして動作するコンフィデンシャルクライアントです。トークン仲介バックエンドは、 鍵ベースのクライアント認証など、コンフィデンシャルクライアント向けの セキュリティベストプラクティスを採用できます。¶
このセクションでは、セクション 5の攻撃シナリオとその影響を再検討し、 追加できる可能性のある防御策について説明します。¶
攻撃者がアプリケーションの実行コンテキスト内で 悪意のあるコードを実行できる場合、次の攻撃シナリオが 該当します。¶
アクセストークンに対する単一実行によるトークン窃取 (セクション 5.1.1)¶
アクセストークンに対する持続的なトークン窃取 (セクション 5.1.2)¶
ユーザーのブラウザを介したリクエストのプロキシ (セクション 5.1.4)¶
これらの攻撃シナリオは、次の 影響をもたらすことに注意してください。¶
盗まれたアクセストークンの悪用 (セクション 5.2.2)¶
クライアントハイジャック (セクション 5.2.3)¶
ブラウザベースアプリケーションへアクセストークンを 露出させることは、トークン仲介バックエンドアーキテクチャパターンの中核となる考え方です。 その結果、アクセストークンは悪意のある ブラウザベースのコードによるトークン窃取に対して脆弱になります。¶
以下に示すほかの攻撃シナリオは、 トークン仲介バックエンドによって効果的に緩和されます。¶
リフレッシュトークンに対する単一実行によるトークン窃取 (セクション 5.1.1)¶
リフレッシュトークンに対する持続的なトークン窃取 (セクション 5.1.2)¶
新しいトークンの取得および抽出 (セクション 5.1.3)¶
トークン仲介バックエンドは、リフレッシュトークンを ブラウザベースアプリケーションに露出させないことで、最初の2つの攻撃 シナリオに対抗します。攻撃者がアプリケーションを完全に制御した場合でも、 盗めるリフレッシュトークンは存在しません。¶
3番目のシナリオでは、攻撃者がサイレントフローを実行して 新しいアクセストークン (および必要に応じてリフレッシュトークン) を取得しますが、 トークン仲介バックエンドをコンフィデンシャルクライアントにすることで緩和されます。 攻撃者が認可コードの取得に成功した場合でも、 クライアントクレデンシャルを持たないため、そのコードを交換できません。さらに、 PKCE を使用すると、認可コードに対するほかの攻撃を防止できます。¶
トークン仲介バックエンドの性質により、 潜在的な攻撃がもたらす次の影響は該当しなくなります。¶
盗まれたリフレッシュトークンの悪用 (セクション 5.2.1)¶
このアーキテクチャは本質的にアクセストークンを露出させますが、 アプリケーションのセキュリティ態勢を 強化するのに役立つ追加の防御策がいくつかあります。¶
トークン仲介バックエンド パターンの性質上、ブラウザ内に永続的なトークンストレージを設ける必要はありません。 必要な場合、アプリケーションは常に Cookie ベースのセッションを使用して、 トークン仲介バックエンドからアクセストークンを取得できます。セクション 8では、 ブラウザ内のさまざまなストレージ 機構のセキュリティ特性について詳しく説明します。¶
アクセストークンが悪意のある ブラウザベースのコードからアクセスできない場所に保存されている場合でも、その悪意のあるコードは、 正規のアプリケーションを模倣してトークン仲介バックエンドへリクエストを送信し、 最新のアクセストークンを取得できることに注意してください。¶
このアーキテクチャで送信者制約付きアクセストークンを使用することは、 単純ではありません。トークン仲介バックエンドは、 認可コードまたはリフレッシュトークンをアクセストークンと交換する責務を負いますが、 アクセストークンを使用するのはアプリケーションです。 DPoP [RFC9449]のような仕組みを使用するには、 2つの当事者に責務を分割する必要がありますが、 このようなシナリオは仕様で定義されていません。このようなシナリオでの DPoP の使用は、 この文書の範囲外です。¶
トークン仲介バックエンドのアーキテクチャは、 ブラウザのみのアプリケーションより複雑ですが、プロキシを行う BFF の運用ほど複雑ではありません。同様に、トークン仲介バックエンドが提供する セキュリティ特性は、BFF の使用と ブラウザのみのアプリケーションの運用との中間に位置します。¶
トークン仲介バックエンドは、 攻撃者にユーザーの代理として長期間アクセスする権限を与える一般的なシナリオに対処します。ただし、 アクセストークン窃取の影響により、攻撃者は依然として リソースサーバーへ直接アクセスできます。¶
トークン仲介バックエンドアーキテクチャを検討する場合、 セクション 6.1で説明した完全な BFF の採用が 実行可能な代替手段であるかどうかを評価することを強く推奨します。 ユースケースまたはシステム要件によって プロキシを行う BFF を使用できない場合に限り、完全な BFF よりもトークン仲介バックエンドを検討すべきです。¶
このセクションでは、OAuth クライアントとして動作し、 OAuth に関するすべての責務をブラウザ内で処理するブラウザベースアプリケーションのアーキテクチャについて説明します。その結果、 ブラウザベースアプリケーションは、バックエンドコンポーネントを介さずに 認可サーバーからトークンを取得します。¶
攻撃者がブラウザ内で悪意のあるコードを実行できる場合、この アプリケーションアーキテクチャは、前述したすべての攻撃シナリオ (セクション 5.1) に対して脆弱です。つまり、 攻撃者は認可サーバーからアクセストークンとリフレッシュトークンを取得できるため、 ユーザーの代理として保護されたリソースへ長期間アクセスできる可能性があります。¶
このアーキテクチャでは、まずコードが静的 Web ホストから ブラウザへ読み込まれ (A)、その後、アプリケーションがブラウザ内で動作します。このシナリオでは、 ブラウザベースアプリケーションはパブリック クライアントと見なされ、認可 サーバーに対する認証に使用できるクライアントクレデンシャルを保持しません。¶
アプリケーションは、PKCE 拡張機能を使用した認可コードフローを開始して、認可コード (B) を取得します (セクション 6.3.2.1で説明)。アプリケーションはブラウザ API (例: [Fetch]) を使用して、 認可コードをトークンと交換するための POST リクエストをトークンエンドポイント (C) へ送信します。¶
その後、アプリケーションは、 セクション 8で説明されているように、適切な ブラウザ API を使用して、アクセストークンと必要に応じて発行されたリフレッシュトークンを可能な限り安全に 保存する責務を負います。¶
ブラウザ内のアプリケーションが リソースサーバーへリクエストを送信するとき、 リソースサーバーと直接やり取りできます。アプリケーションはリクエスト (D) に アクセストークンを含め、 リソースサーバーからレスポンス (E) を受け取ります。¶
パブリッククライアント (セクション 2.1([RFC6749])で定義) であり、 セクション 4.1([RFC6749])で説明されている 認可コードグラントタイプを使用するブラウザベースアプリケーションは MUST、このセクションで説明する追加要件にも 従わなければなりません。¶
パブリッククライアントであるブラウザベースアプリケーションは MUST、アクセストークンを取得するときに PKCE [RFC7636]拡張機能を実装しなければならず、 認可サーバーは MUST、このようなクライアントに対して PKCE をサポートし、適用しなければなりません。¶
PKCE 拡張機能は、 認可コードを交換する クライアントインスタンスがフローを開始したものと同一であることを 認可サーバーが検証できるようにすることで、認可コードが傍受され、 悪意のあるクライアントによってアクセストークンと交換される攻撃を防止します。¶
ブラウザベースアプリケーションは MUST、 リダイレクト URI に対する CSRF 攻撃を防止しなければなりません。これは、 次のいずれかによって実現できます。¶
このクライアントに対して PKCE を必須とするよう認可サーバーを設定する¶
CSRF トークンを伝達するため、OAuth の
state パラメーターに一意の値を使用して検証する¶
アプリケーションが OpenID Connect も
使用する場合、[OpenID]で説明されている OpenID Connect の
nonce パラメーターを使用して検証する¶
認可コードフローに適した CSRF 防御の選択に関する詳細については、セクション 2.1([RFC9700])を参照してください。¶
ブラウザベースクライアントの場合、アプリケーションが明示的に DPoP [RFC9449]を使用しない限り、リフレッシュトークンは通常 ベアラートークンです。その結果、漏洩したリフレッシュトークンの リスクは漏洩したアクセストークンより高くなります。攻撃者が盗んだリフレッシュトークンを使用し続け、 認可サーバーに 検出されることなく新しいアクセストークンを取得できる可能性があるためです。¶
認可サーバーは、ブラウザベース アプリケーションへリフレッシュトークンを発行するかどうかを選択できます。ただし、 サードパーティー Cookie ブロック機構の影響を考慮すると、 リフレッシュトークンの使用は大幅に魅力的になっています。 [RFC9700]では、 [RFC6749]の推奨事項に加えて、リフレッシュトークンに関する 追加要件がいくつか説明されています。この BCP に準拠するアプリケーションと認可 サーバーは MUST、ブラウザベースアプリケーションにリフレッシュトークンを発行する場合、 リフレッシュトークンに関する [RFC9700]の 推奨事項にも従わなければなりません。¶
具体的には、認可サーバーは次の要件に従わなければなりません。¶
MUST、 使用するたびにリフレッシュトークンをローテーションするか、セクション 4.14.2([RFC9700])で説明されている 送信者制約付きリフレッシュトークンを使用しなければならない¶
MUST、 リフレッシュトークンに最大有効期間を設定するか、一定期間 リフレッシュトークンが使用されなかった場合に期限切れにしなければならない¶
MUST NOT、 リフレッシュトークンに事前設定された有効期限がある場合、 ローテーションされたリフレッシュトークンの発行時に、新しいリフレッシュ トークンの有効期間を最初のリフレッシュトークンの有効期間より先まで 延長してはならない¶
リフレッシュトークン全体の有効期間を 最初のリフレッシュトークンの有効期間に制限すると、盗まれたリフレッシュトークンが 無期限に使用されることを防止できます。¶
例:¶
ユーザーがアプリケーションを認可し、 有効期間10分のアクセストークンと有効期間8 時間のリフレッシュトークンが発行される。¶
10分後、最初のアクセストークンが 期限切れになるため、アプリケーションはリフレッシュトークンを使用して新しいアクセス トークンを取得する。¶
認可サーバーは、有効期間10分の新しいアクセス トークンと、有効期間7時間 50分の新しいリフレッシュトークンを返す。¶
これは、最初の 認可から8時間が経過するまで続く。¶
この時点で、アプリケーションが 8時間後にリフレッシュトークンを使用しようとすると、リクエストは失敗し、 アプリケーションはユーザーの認証または以前に確立されたセッションを利用する 認可コードフローを再度開始する必要がある。¶
認可サーバーは SHOULD、 リフレッシュトークンの有効期間を、認可サーバーにおけるユーザーの認証済みセッションに 関連付けるべきです。これにより、ユーザーがログアウトしたとき、 ブラウザベースアプリケーションへ以前に発行されたリフレッシュトークンが無効になり、 シングルログアウトのシナリオを再現できます。認可サーバーは MAY、 ほかのパブリッククライアントとは異なる、ブラウザベースアプリケーション向けの リフレッシュトークンの発行、有効期間、および期限切れに関するポリシーを設定してもかまいません。¶
ブラウザベースアプリケーションのソースコードは、 エンドユーザーの ブラウザへ配信されるため、事前設定されたシークレットを含めるのには適していません。その結果、 ブラウザベースアプリケーションは通常、セクション 2.1([RFC6749])で定義される パブリッククライアントとして展開されます。¶
複数のユーザーへ配布されるアプリケーションの一部として
静的に含まれるシークレットは、
機密のシークレットとして扱うべきではありません。各
ユーザーが自身のコピーを調査して、共有シークレットを知ることができるためです。この
理由と、セクション 5.3.1([RFC6819])に記載された理由から、認可
サーバーは MUST NOT、共有シークレットを使用したブラウザベース
アプリケーションのクライアント認証を要求してはなりません。この認証は、
client_id パラメーターによってすでに提供される
クライアント識別以上の価値を持たないためです。¶
SPA クライアントに静的に含まれる共有 シークレットを依然として要求する認可サーバーは MUST、そのクライアントを パブリック クライアントとして扱い、シークレットをクライアントの身元証明として受け入れてはなりません。 追加の対策がない場合、このようなクライアントはクライアント偽装の対象となります (セクション 6.3.3.2を参照)。¶
セクション 10.2([RFC6749])に記載されているように、認可 サーバーは SHOULD NOT、クライアントアプリケーションの身元を 保証できる場合を除き、ユーザーの同意または操作なしで 認可リクエストを自動的に処理すべきではありません。¶
認可サーバーがリダイレクト URI を、
ワイルドカードドメイン、ワイルドカードパス、またはワイルドカード
クエリ文字列コンポーネントを使用できない、固定された絶対
HTTPS URI の集合に制限している場合、認可サーバーは、
登録された絶対 HTTPS URI との完全一致を MAY、
client_idに対する以前のリクエストが
すでに承認されているときに、認可リクエストを
自動処理するかどうかを判断するためのクライアントの身元証明として
受け入れてもかまいません。¶
クライアントは MUST、 1つ以上のリダイレクト URI を認可サーバーに登録し、 認可リクエストでは完全に一致する登録済みリダイレクト URI だけを使用しなければなりません。¶
認可サーバーは MUST、セクション 4.1.1([RFC9700])で説明されているように、登録済み リダイレクト URI との完全一致を要求しなければなりません。これは、 認可コードを標的とする攻撃の防止に役立ちます。¶
ブラウザベースアプリケーションでは、 プライマリウィンドウをリダイレクトする代わりに、 ポップアップや iframe などのセカンダリウィンドウで OAuth フローを実行することが一般的です。 このようなフローでは、ページの再読み込みを避けたり、 フレームベースのフローをサイレントに実行したりするため、ブラウザベースアプリケーションが プライマリウィンドウの制御を保持します。¶
ブラウザベースアプリケーションと認可サーバーが 異なるフレームで呼び出される場合、トップレベルの リダイレクトの代わりに、postMessage API (別名 [WebMessaging]) などのブラウザ内通信技術を使用する必要があります。 メッセージの機密性と真正性を保証するため、postMessage の送信元 オリジンと受信側オリジンの両方を MUST、 postMessage API が本来提供する仕組みを使用して検証しなければなりません ([WebMessaging]のセクション 9.3.2)。¶
セクション 4.17([RFC9700])では、 ブラウザ内通信フローのセキュリティと、これらの攻撃を防ぐために ブラウザベースアプリケーションおよび認可サーバーが MUST 適用しなければならない対策について詳しく説明しています。¶
このシナリオでは、アプリケーションはブラウザ API を使用して、 認可サーバーとリソースサーバーへリクエストを送信します。 OAuth 2.0 の性質上、これらは通常クロスオリジンリクエストであり、 ブラウザによって適用されるクロスオリジン通信の制限を受けます。 認可サーバーとリソースサーバーは MUST、 アプリケーションが必要なクロスオリジンリクエストを送信できるようにするため、 必要な CORS ヘッダー ([Fetch]で定義) を送信しなければなりません。 ブラウザベース OAuth クライアントが 認可サーバーまたはリソースサーバーと同じオリジンで動作する例外的なシナリオでは、 必要なやり取りを有効にするための CORS ポリシーは不要であることに注意してください。¶
認可サーバーの場合、CORS 設定は、 ブラウザベースアプリケーションが認可コードをトークンと交換するトークンエンドポイントに 関係します。さらに、認可サーバーが ディスカバリメタデータ URL、JSON Web Key Set、動的クライアント登録、失効、イントロスペクション、 またはユーザー情報エンドポイントなどの追加エンドポイントをアプリケーションへ提供する場合、 ブラウザベースアプリケーションがこれらのエンドポイントにアクセスすることもあります。そのため、認可サーバーは、 これらのエンドポイントで CORS をサポートする責務を負います。¶
この仕様には、 ワイルドカードオリジンまたはより制限的な設定で構成できる 具体的な CORS ポリシーの実装を決定するためのガイドラインは含まれていません。CORS には、 異なるセキュリティ特性を持つ2つの動作モードがあることに注意してください。最初のモードは、 CORS セーフリストリクエスト (以前は単純リクエストと呼ばれていた) に適用されます。この場合、ブラウザは リクエストを送信し、CORS レスポンスヘッダーを使用して、 レスポンスをクライアント側の実行コンテキストへ公開できるかどうかを判断します。 カスタムリクエストヘッダーを含むリクエストなど、CORS セーフリストにない リクエストの場合、ブラウザは最初にプリフライトを使用して CORS ポリシーを 確認します。サーバーがプリフライトレスポンスで承認を返した場合に限り、ブラウザは 実際のリクエストを送信します。¶
認可サーバー固有の 設定によっては、プリフライトへの CORS レスポンスが 実際のリクエストへの CORS レスポンスと異なる場合があることに注意してください。プリフライト中、 認可サーバーは提供されたオリジンだけを検証できますが、実際の リクエストでは、クライアント ID などの完全なリクエストデータを利用できます。 そのため、認可サーバーはプリフライト中に既知のオリジンを承認したうえで、 そのオリジンを特定のクライアントの事前登録済みオリジン一覧と比較し、 実際のリクエストを拒否できます。¶
このセクションでは、セクション 5の攻撃シナリオとその影響を再検討し、 追加できる可能性のある防御策について説明します。¶
攻撃者がアプリケーションの実行コンテキスト内で 悪意のあるコードを実行できる場合、次の攻撃シナリオが 該当します。¶
単一実行によるトークン窃取 (セクション 5.1.1)¶
持続的なトークン窃取 (セクション 5.1.2)¶
新しいトークンの取得および抽出 (セクション 5.1.3)¶
ユーザーのブラウザを介したリクエストのプロキシ (セクション 5.1.4)¶
最も危険な攻撃シナリオは、新しいトークンの取得および 抽出です。この攻撃シナリオでは、攻撃者は認可サーバーとのみやり取りするため、 クライアント内の OAuth 機能の実際の実装詳細は 関係しません。正規のクライアント アプリケーションがトークンを攻撃者から完全に分離する方法を見つけた場合でも、 攻撃者は認可サーバーからトークンを取得できます。¶
これらの攻撃シナリオは、次の 影響をもたらすことに注意してください。¶
盗まれたリフレッシュトークンの悪用 (セクション 5.2.1)¶
盗まれたアクセストークンの悪用 (セクション 5.2.2)¶
クライアントハイジャック (セクション 5.2.3)¶
このアーキテクチャは本質的に 悪意のあるブラウザベースのコードに対して脆弱ですが、アプリケーションのセキュリティ態勢を 強化するのに役立つ追加の防御策がいくつかあります。これらの 防御策はいずれも、攻撃者が新しいフローを実行して トークンを取得できるという根本的な問題に対処したり、その問題を解決したりするものではないことに注意してください。¶
トークンを直接処理する場合、アプリケーションは、 アクセストークンと リフレッシュトークンを保存するためのさまざまなストレージ機構を選択できます。ローカル ストレージ [WebStorage]など、どこからでもアクセスできるストレージ領域は、 Web Worker [WebWorker]など、より分離されたストレージ領域よりも、 悪意のある JavaScript から容易にアクセスできます。セクション 8では、 さまざまなストレージ機構とそのトレードオフについて 詳しく説明します。¶
実用的な実装パターンでは、Web Worker [WebWorker]を使用してリフレッシュ トークンを分離し、リソースサーバーへのリクエストを送信するアプリケーションへ アクセストークンを提供できます。これにより、攻撃者が アプリケーションのリフレッシュトークンを使用して新しいトークンを取得することを防止できます。¶
ただし、トークンを攻撃者から 完全に分離するトークンストレージ機構であっても、攻撃者が 新しいフローを実行して新しい一組のトークンを取得することは防止できません (セクション 5.1.3を参照)。¶
ブラウザベース OAuth クライアントは DPoP [RFC9449]を実装して、 ベアラーアクセストークンとベアラーリフレッシュトークンから 送信者制約付きトークンへ移行できます。このような実装では、DPoP 証明への 署名に使用する秘密鍵をブラウザが処理します (抽出不可能な [CryptoKeyPair]を [W3C.IndexedDB]を使用して保存)。その結果、 DPoP を使用すると、XSS 攻撃者が アプリケーションのトークンを外部へ持ち出すシナリオを効果的に防止できます (セクション 5.1.1 および 5.1.2を参照)。¶
DPoP を使用しても、 攻撃者が新しいフローを実行して新しいアクセストークン (および必要に応じてリフレッシュトークン) を取得することは防止できない点に注意してください。セクション 5.1.3を参照してください。DPoP が 必須の場合でも、攻撃者は新しい一組のトークンを自身が制御する鍵ペアへ バインドできます。これにより、攻撃者は送信者制約付き トークンを外部へ持ち出し、攻撃者が制御する鍵を使用して 必要な DPoP 証明を計算することで、そのトークンを使用できます。¶
クライアントアプリケーションとバックエンドアプリケーションは、過去 20 年間で大きく進化してきた。脅威、攻撃者モデル、そして現代のアプリケーション セキュリティに対する一般的な理解も同様である。その結果、業界で一般に受け入れられてきた従来の推奨事項や OAuth ワーキンググループが公開した推奨事項は、現在では推奨されないことが多く、提案されたソリューションも 期待されるセキュリティ要件を満たさないことが多い。¶
本セクションでは、現代のブラウザベース OAuth アプリケーションでの使用が 推奨されない、いくつかの代替アーキテクチャパターンについて説明する。また、それぞれの パターンについて、関連する場合には攻撃シナリオとその結果を調査する脅威分析とともに 説明する。¶
単純なアプリケーションで、セッション管理という概念の代わりに OAuth を 使用することにより、不必要に複雑化している例があまりにも多い。典型例は、 サーバーサイドの Model-View-Controller(MVC)アプリケーションを現代化したもので、現在では サーバーサイド API をバックエンドとするブラウザベースのフロントエンドで構成される。¶
このようなアプリケーションでは、OpenID Connect を使用してユーザー 認証を専用プロバイダーに委任することで、アプリケーションのアーキテクチャ と開発を大幅に簡素化できる。しかし、フロントエンドと バックエンド間のアクセスを制御するために OAuth を使用する必要はないことが多い。 アクセストークンを使用する代わりに、アプリケーションは サーバーサイドの Cookie ベースのセッション状態を利用して、ユーザーの認証状態を追跡できる。 セッション Cookie を保護するためのセキュリティガイドラインについては、セクション 6.1.3.2で説明する。¶
OAuth を使用しないという助言は本書では場違いに見えるかもしれないが、 OAuth はもともと API へのサードパーティまたはフェデレーションアクセスのために作成されたため、 単一の共通ドメインへのデプロイメントには最適なソリューションでない可能性があることに 留意することが重要である。とはいえ、共通ドメインアーキテクチャであっても OAuth を使用することには、次のようないくつかの利点がある。¶
将来の柔軟性が高まる。たとえば、後から システムに新しいドメインを追加する場合、すでに OAuth が導入されていれば、新しい ドメインのためにアーキテクチャを追加で再設計する必要がない。¶
統合のための専用コードを作成するのではなく、 既存のライブラリサポートを利用できる。¶
ログイン、多要素認証のサポート、 アカウント管理、および復旧をアプリケーションロジックの一部とせず、 OAuth サーバーに集約できる。¶
ユーザーを認証する責務と リソースを提供する責務を分離できる。¶
ファーストパーティの同一ドメインというシナリオでブラウザベースアプリケーションに OAuth を使用すると、これらの利点が得られ、前述したどのアーキテクチャパターンでも 実現できる。¶
OAuth を使用しないため、このアーキテクチャパターンが 脆弱なのは、ユーザーのブラウザを介したリクエストのプロキシという 攻撃シナリオ(セクション 5.1.4)のみである。その結果、 このパターンは、クライアントハイジャック(セクション 5.2.3)という結果を 引き起こす可能性がある。¶
OAuth 2.0 インプリシットグラントタイプ(セクション 4.2( [RFC6749]) で定義)は、認可コードを交換するステップを経ずに、認可サーバーが 認可レスポンス(フロントチャネル)でアクセストークンを発行することで 機能する。この場合、アクセス トークンはリダイレクト URI のフラグメント部分で返されるため、攻撃者には アクセストークンを傍受して窃取する機会が複数与えられる。¶
インプリシットグラントタイプのセキュリティ特性により、これはもはや 推奨されるベストプラクティスではない。このフローの使用を実効的に防ぐため、認可サーバーは MUST NOT 認可レスポンスでアクセストークンを発行してはならず、MUST トークンエンドポイントからのみ アクセストークンを発行しなければならない。ブラウザベースクライアントはMUST認可コードグラントタイプを使用し、アクセストークンを取得するためにMUST NOTインプリシットグラントタイプを使用してはならない。¶
歴史的に、インプリシットグラントタイプは ブラウザベースアプリケーションに利点をもたらしていた。これは、 JavaScript がページの再読み込みを発生させずに、いつでも URL のフラグメント部分を任意に読み取り、 操作できたためである。アプリがアクセストークンを取得した後に、 URL からアクセストークンを削除するには、これが必要だった。さらに、 CORS がブラウザに広く普及するまで、インプリシットグラントタイプは、 ブラウザまたはサーバーでの CORS サポートを必要としない代替フローを 提供していた。¶
現代のブラウザには、現在 Session History API( 「ナビゲーションとセッション履歴」 ([HTML])で説明)があり、これはページの再読み込みを発生させずに URL のパスおよびクエリ文字列 コンポーネントを変更するための仕組みを提供する。さらに、CORS は広く サポートされ、シングルページアプリケーションで多くの目的に使用されている。つまり、 現代のブラウザベースアプリケーションは、PKCE を使用した OAuth 2.0 認可コードグラントタイプを 使用できる。Session History API によりページの再読み込みを発生させずに クエリ文字列から認可コードを削除できることに加え、トークンエンドポイントが CORS をサポートしていれば、認可サーバーが異なるドメインにあっても アプリはトークンを取得できるためである。¶
本セクションで説明するアーキテクチャパターンは、 次の攻撃シナリオに対して脆弱である。¶
単一実行によるトークン窃取(セクション 5.1.1)¶
持続的なトークン窃取(セクション 5.1.2)¶
新しいトークンの取得および抽出(セクション 5.1.3)¶
ユーザーのブラウザを介したリクエストのプロキシ(セクション 5.1.4)¶
その結果、このパターンは次の結果を引き起こす可能性がある。¶
窃取されたリフレッシュトークンの悪用(セクション 5.2.1)¶
窃取されたアクセストークンの悪用(セクション 5.2.2)¶
クライアントハイジャック(セクション 5.2.3)¶
すでに説明した攻撃シナリオと結果のほかにも、 インプリシットグラントタイプの廃止をさらに裏付ける追加の攻撃がいくつかある。[RFC6819]およびセクション 4.1.2([RFC9700]) で説明されているインプリシットグラントタイプに対する攻撃の多くには、 十分な緩和策がない。以下のセクションでは、インプリシットグラントタイプを使用し続ける限り 緩和できない具体的な 攻撃について説明する。¶
攻撃者が認可レスポンスを 自身の管理下にある URI に送信させることができれば、 アクセストークンを含む認可レスポンスに直接アクセスできる。 この攻撃を実行するいくつかの方法が、[RFC9700]で詳しく説明されている。¶
攻撃者はブラウザ履歴から アクセストークンを取得できる可能性がある。 [RFC6819]で推奨される対策は、トークンに短い 有効期限を 使用することと、ブラウザがレスポンスをキャッシュしないようにすることに限られる。 いずれもこの攻撃を完全には防止せず、潜在的な 被害を軽減するだけである。¶
さらに、現在では多くのブラウザがブラウザ履歴を クラウドサービスや 複数のデバイスにも同期するため、URL からアクセストークンを抽出できる 攻撃対象領域がさらに広がる。¶
これについては、セクション 4.3.2([RFC9700])で詳しく説明されている。¶
攻撃者は、さまざまな 手段でページを変更したり、ブラウザにスクリプトを挿入したりできる可能性がある。これには、 たとえば企業ネットワークによってブラウザの HTTPS 接続が傍受される場合も 含まれる。TLS レイヤーに対する攻撃の防止は、通常、 基本的なセキュリティ推奨事項の範囲外 であるが、ブラウザベースアプリケーションの場合、 このような攻撃ははるかに実行しやすい。挿入されたスクリプトにより、攻撃者は ページ上のすべてのものにアクセスできる可能性がある。¶
アプリケーションが既知の標準的な方法で
アクセストークンを取得している場合、ページ上で悪意のあるスクリプトが実行されるリスクが
増幅される可能性がある。つまり、攻撃者はいつでも window.location 変数を確認して
アクセストークンを見つけられる。この脅威プロファイルは、
認可コードフローを介して取得されたアクセストークンがどこに、またはどのように
保存されるかを把握したうえで、攻撃者が特定のアプリケーションを
標的にする場合とは異なる。¶
ブラウザベースアプリケーションで、 分析ツール、クラッシュレポート、さらにはソーシャルメディアの「いいね」 ボタンなどのサードパーティスクリプトを使用することは比較的一般的である。 このような状況では、アプリケーションの作成者が、アプリケーションで実行される コード全体を 完全に把握できない可能性がある。アクセストークンがフラグメントで 返されると、ページ上のすべてのサードパーティスクリプトから見える。¶
推奨される認可コードグラントタイプの使用と比較して、 インプリシットグラントタイプが不利となる理由はほかにもいくつかある。¶
OAuth 2.0 には、特定のアクセストークンが そのクライアントを対象としていたことを、クライアントが 検証する仕組みがない。そのため、悪意のある者が他の 手段で取得したアクセストークンを クライアントに渡した場合、悪用やなりすまし攻撃につながる可能性がある。¶
フロントチャネルのリダイレクトでアクセストークンを返しても、 このリダイレクトはさまざまな方法で失敗したり傍受されたりする可能性があるため、 アクセストークンが実際にアプリケーションへ到達するという保証を認可 サーバーは得られない。¶
インプリシットグラントタイプをサポートするには、追加の コード、より多くの保守作業、そして 関連するセキュリティ上の考慮事項への理解が必要となる。認可 サーバーを認可コードグラントタイプだけに限定すると、実装の 攻撃対象領域が縮小する。¶
ブラウザベースアプリケーションが ネイティブアプリにラップされる場合、いずれにしても[RFC8252]は PKCE を使用した認可コードグラントタイプの使用を要求している。¶
リソースオーナーパスワードクレデンシャルグラントは、MUST NOT使用してはならない。これは、 セクション 2.4( [RFC9700])で説明されている。代わりに、認可 コードグラントタイプを使用し、ユーザーを認可サーバーへ リダイレクトすることで、認可サーバーはユーザーに 安全でフィッシング耐性のある認証オプションを提示したり、シングルサインオンセッションを活用したり、 サードパーティのアイデンティティプロバイダーを使用したりできる。これに対して、リソースオーナーパスワードクレデンシャル グラントには これらの仕組みが組み込まれておらず、独自 プロトコルによって拡張する必要がある。¶
このベストプラクティスに準拠するため、OAuth または OpenID Connect を使用するブラウザベースアプリケーションは、本文書で説明するリダイレクトベースのフロー(たとえば OAuth 認可 コードグラントタイプ)を MUST使用しなければならない。¶
攻撃者が既存のトークンを抽出したり、 新しい一連のトークンを取得したりする能力を制限するため、Service Worker [W3C.service-workers]を使用するパターンが 過去に提案されていた。このパターンでは、アプリケーションが読み込まれたときの最初の動作として Service Worker を登録する。Service Worker は、トークンを取得するための 認可コードフローの実行と、リソースサーバーへの送信リクエストに 適切なアクセストークンを付加する役割を担う。さらに、Service Worker は、クライアントアプリケーションの コードが認可サーバーのエンドポイントを直接呼び出すことを阻止する。この制限は、 「新しいトークンの取得および抽出」という攻撃シナリオ(セクション 5.1.3)への対処を目的としている。¶
以下のシーケンス図は、クライアント、 Service Worker、認可サーバー、およびリソースサーバー間のやり取りを示している。¶
このパターンは、ブラウザで実行されるアプリケーションに トークンを一切公開しないことに注意すること。Service Worker は隔離された実行環境で動作するため、 共有メモリは存在せず、クライアントアプリケーションが Service Worker の実行に影響を及ぼす方法もない。¶
本セクションで説明するアーキテクチャパターンは、 次の攻撃シナリオに対して脆弱である。¶
新しいトークンの取得および抽出(セクション 5.1.3)¶
ユーザーのブラウザを介したリクエストのプロキシ(セクション 5.1.4)¶
その結果、このパターンは次の結果を引き起こす可能性がある。¶
窃取されたリフレッシュトークンの悪用(セクション 5.2.1)¶
窃取されたアクセストークンの悪用(セクション 5.2.2)¶
クライアントハイジャック(セクション 5.2.3)¶
Service Worker の使用によって得られる一見有望な セキュリティ上の利点については、そのセキュリティ上の制約をさらに詳しく検討する必要がある。 関連する攻撃シナリオ(セクション 5.1)からアプリケーションを 完全に保護するには、Service Worker が次の 2 つのセキュリティ要件を満たす必要がある。¶
登録されると、Service Worker は認可 コードフローを実行してトークンを取得する。Service Worker は自身の隔離された実行環境で トークンを管理するため、潜在的に悪意のあるコードを含め、 どのアプリケーションコードもトークンにアクセスできない。したがって、 Service Worker は、トークンの外部流出を防止するという最初の要件を満たす。 これにより、セクション 5.1で説明した最初の 2 つの攻撃シナリオは実質的に無効化される。¶
2 番目のセキュリティ要件を満たすには、Service Worker は、正規のアプリケーションを制御する攻撃者が新しい認可コードグラントを 実行できないことを保証できなければならない。この攻撃については、 セクション 5.1.3で説明している。Service Worker の性質上、登録済みの Service Worker は、そのような新しいフローを開始するすべての送信リクエストを、 フレームまたは新しいウィンドウ内で発生した場合でも阻止できる。¶
しかし、アプリケーション内で実行される悪意のあるコードは、 この Service Worker の登録を解除できる。Service Worker の登録解除は アプリケーションの機能に重大な影響を与える可能性があるため、ブラウザが 軽々しく処理する操作ではない。そのため、登録解除された Service Worker は そのようにマークされるが、現在実行中のすべてのインスタンスは、それぞれに対応する ブラウジングコンテキストが終了するまで(たとえばタブまたは ウィンドウを閉じるまで)アクティブなままである。したがって、攻撃者が Service Worker の登録を解除しても、 その Service Worker はアクティブなままであり、攻撃者が認可 サーバーへ到達することを阻止できる。¶
Service Worker の登録を解除した場合の結果の 1 つは、 新しいブラウジングコンテキストが開かれたときに、その Service Worker が存在しないことである。したがって、 攻撃者が最初に Service Worker の登録を解除してから、フレーム内で新しいフローを 開始すると、そのフレームのブラウジングコンテキストに関連付けられた Service Worker は 存在しない。その結果、攻撃者は独自の新しい 認可コードグラントを実行し、フレームの URL から認可コードを抽出して、 トークンと交換できる。要するに、Service Worker は 2 番目のセキュリティ要件を満たせず、攻撃者が新しい一連のトークンを 取得するシナリオ(セクション 5.1.3)に対して脆弱なままとなる。¶
これらの欠点に加え、Service Worker の登録と 維持には相当な複雑さが伴うため、このパターンは 推奨されない。¶
最後に、Service Worker の使用それ自体が アプリケーションの攻撃対象領域を拡大するわけではないことに注意すること。実際には、Service Worker は、送信リクエストに OAuth アクセストークンを含める機能を レガシーアプリケーションへ後付けするために使用されることが多い。このような シナリオにおける Service Worker は、アプリケーションのセキュリティ特性を変更するのではなく、 単にアプリケーションの開発と保守を簡素化する。¶
ブラウザベースアプリケーションが OAuth アクセストークンまたはリフレッシュトークンを直接処理する場合、 そのアプリケーションはトークンを一時的または永続的に保存する責任を負う。そのため、 アプリケーションはトークンの管理方法(メモリ内ストレージか永続ストレージかなど)と、 トークンをメインのアプリケーションコードからさらに分離するための手順を決定する必要がある。本セクションでは、 いくつかの異なるストレージメカニズムとその特性について説明する。これらの推奨事項では、 OAuth トークン固有の特性を考慮しており、その一部は一般的なブラウザセキュリティの推奨事項と重複する場合がある。¶
ブラウザベースのトークンストレージソリューションのセキュリティ特性を論じる際には、 攻撃者がブラウザベースアプリケーションを侵害したときに持つ能力を理解することが 重要である。これまでの説明と同様に、主に次の 2 つの攻撃シナリオを考慮する必要がある。¶
攻撃者のコードは正規のアプリケーションコードと区別できなくなるため、 攻撃者は常に正規のアプリケーションコードとまったく同じ方法で プロバイダーにトークンを要求できる。その結果、完全に分離されたトークンストレージソリューションであっても、 攻撃者がプロバイダーへトークンを要求するという第 2 の脅威に伴う危険には 対処できない。¶
とはいえ、ブラウザベースの各ストレージソリューションが持つセキュリティ特性の違いは、 攻撃者がストレージから既存のトークンを取得する能力に影響する。¶
Service Worker [W3C.service-workers]は、トークンを管理するための完全に分離された環境を 提供する。クライアントアプリケーションはこれらのトークンにアクセスできないため、外部流出から 効果的に保護される。これらのトークンのセキュリティを保証するため、Service Worker はトークンをアプリケーションと共有できない。したがって、アプリケーションが トークンを使用する操作を実行する場合は、Service Worker に その操作の実行と結果の返却を依頼する必要がある。¶
アプリケーションの実行コンテキストからトークンを分離する場合、 Service Worker は、メインウィンドウと共有される永続ストレージ API にトークンを MUST NOT保存してはならない。たとえば、現在の IndexedDB ストレージは ブラウジングコンテキストと Service Worker の間で共有されるため、 メインウィンドウからアクセスできない状態を維持すべきデータを Service Worker が 永続化する場所としては適していない。したがって、現在の Service Worker は 分離された永続ストレージ領域へアクセスできない。¶
前述したように、Service Worker を使用しても、攻撃者が 新しい一連のトークンを取得することは防止できない。同様に、アプリケーションが認可サーバーから トークンを取得し、その後の管理のために Service Worker へ渡す責任を負う場合、 攻撃者も正規のアプリケーションと同じ操作を行って これらのトークンを取得できる。¶
アプリケーションは Web Worker [WebWorker]を使用できる。これは Service Worker に依存する前述のシナリオと ほぼ同じシナリオとなる。Service Worker と Web Worker の違いは、アクセスレベルと実行時の特性にある。Service Worker は 送信リクエストを傍受して変更できるが、Web Worker はバックグラウンドタスクを実行する手段にすぎない。Web Worker は一時的なもので、ブラウジングコンテキストが閉じられると消滅する。一方、Service Worker は ブラウザに登録される永続的なサービスである。¶
Web Worker を使用した場合のセキュリティ特性は、Service Worker を使用した場合と同じである。トークンがアプリケーションに公開されると脆弱になる。トークンを 使用する必要がある場合、そのトークンに依存する操作は Web Worker が実行しなければならない。¶
リフレッシュトークンを分離する一般的な方法の 1 つは、Web Worker を使用することである。このような シナリオでは、アプリケーションは Web Worker から認可コードフローを開始する。 リダイレクトから取得した認可コードは Web Worker に転送され、Web Worker がそのコードを トークンと交換する。Web Worker はリフレッシュトークンをメモリ内に保持し、アクセストークンをメインの アプリケーションへ送信する。メインアプリケーションは必要に応じてアクセストークンを使用する。アプリケーションが リフレッシュトークンフローを実行する必要がある場合、Web Worker に実行を依頼し、その後 アプリケーションは新しいアクセストークンを取得する。¶
このシナリオでは、アプリケーション自身のリフレッシュトークンは外部流出から 効果的に保護されるが、アクセストークンは保護されない。さらに、新しい認可コードフロー(セクション 5.1.3)を実行して、 攻撃者が自身のトークンを取得することを妨げるものは何もない。¶
もう 1 つの選択肢は、永続 ストレージを使用せずにトークンをメモリ内へ保持することである。これにより、トークンが公開される範囲を現在の実行コンテキストだけに 制限できるが、ページを読み込むたびにトークンを維持できないという欠点がある。¶
JavaScript 実行環境では、クロージャ変数を使用してトークンを直接 アクセスから効果的に保護することで、メモリ内トークンストレージのセキュリティをさらに強化できる。 クロージャを使用すると、トークンへアクセスできるのは、リソースサーバーへリクエストを送信する関数など、 クロージャ内であらかじめ定義された関数だけとなる。¶
クロージャは単純で分離された環境では適切に機能するが、
ブラウザの実行環境のような複雑な環境で安全に保つことは難しい。たとえば、クロージャは
操作を実行するために、
toString やネットワーク API など、さまざまな外部関数に依存する。プロトタイプ汚染を使用すると、攻撃者は
これらの関数を悪意のあるバージョンに置き換え、以降のクロージャの操作で
その悪意のあるバージョンを使用させることができる。悪意のある関数内では、攻撃者は関数の
引数にアクセスできるため、クロージャ内のトークンが攻撃者に公開される可能性がある。¶
本書執筆時点でブラウザから利用できる永続ストレージ API は、 localStorage [WebStorage]、 sessionStorage [WebStorage]、および IndexedDB [W3C.IndexedDB]である。¶
localStorage はページを再読み込みしても永続し、すべてのタブで共有される。この ストレージにはオリジン全体からアクセスでき、長期間保持される。攻撃者は 同じオリジン内でコードを実行するため、localStorage は悪意のある JavaScript による不正アクセスを 防止できず、攻撃者は localStorage の内容を読み取ることができる。さらに、localStorage は同期 API であり、 操作が完了するまで他の JavaScript をブロックする。¶
sessionStorage は localStorage と似ているが、 sessionStorage の存続期間はブラウザタブの存続期間に関連付けられる。さらに、sessionStorage は 同一オリジンのページを開いている複数のタブ間で共有されないため、 sessionStorage 内のトークンが公開される範囲がわずかに縮小される。¶
IndexedDB は localStorage と同様の永続ストレージメカニズムだが、 複数のタブ間だけでなく、ブラウジングコンテキストと Service Worker の間でも共有される。 さらに、IndexedDB は非同期 API であり、同期型の localStorage API よりも推奨される。¶
これらのパターンの主な違いはデータが公開される範囲にあるが、 攻撃者がアプリケーションの実行環境で悪意のあるコードを 実行できる場合、どの選択肢もトークンの外部流出を完全には緩和できない。¶
いずれの場合も、本書執筆時点では、ブラウザストレージの保存データが 暗号化されるという保証はない。この動作により、ディスク上のファイルを 読み取る能力を持つ攻撃者にトークンが公開される可能性がある。このような攻撃は ブラウザベースアプリケーションの範囲をはるかに超える能力に依存するものの、この問題は 現代のアプリケーションに対する重要な攻撃経路を浮き彫りにする。ユーザーのマシンを探索し、 高価値のトークンやセッション Cookie を取得するためにブラウザプロファイルを探し、 アカウント乗っ取り攻撃を引き起こすマルウェアがますます増えている。¶
ブラウザベースアプリケーションがこのような攻撃を緩和することはできないが、 暗号化によってデータの機密性を確保することで、攻撃の結果を緩和できる。 [W3C.WebCryptoAPI]は、JavaScript コードが 秘密鍵を生成するための仕組みと、その鍵をエクスポート不可にする選択肢を提供する。JavaScript アプリケーションは、この API を使用して、保存する前にトークンを暗号化し、使用時に復号できる。しかし、 [W3C.WebCryptoAPI] 仕様が保証するのは、ブラウザコードへ鍵をエクスポートできないことだけであり、 オペレーティングシステムにおける鍵自体の基盤ストレージについては要件を 課していない。そのため、エクスポート不可の鍵を、基盤となるファイルシステムからの 外部流出を防ぐ手段として信頼することはできない。¶
ファイルシステムからのトークン流出を防ぐには、 暗号鍵をリモート サーバーなど、ファイルシステム以外の場所へ保存する必要がある。これは純粋なブラウザベースアプリに 新たな複雑さをもたらすため、本文書の範囲外である。¶
本文書全体で説明してきたように、送信者制約付きトークンを使用しても、 ブラウザ専用 OAuth クライアントのセキュリティ上の制約は解決されない。しかし、トークン仲介バックエンド(セクション 6.2)またはブラウザ専用 OAuth クライアント(セクション 6.3)が提供する セキュリティレベルで対象のユースケースに十分な場合、送信者制約付きトークンを使用して、 アクセストークンとリフレッシュトークンの両方のセキュリティを強化できる。ブラウザベースアプリケーションから 使用できる方法で送信者制約付きトークンを実装する手段の 1 つが、DPoP [RFC9449]である。¶
送信者制約付きトークンを使用する場合、OAuth クライアントは トークンを使用するために秘密鍵の所有を証明しなければならず、トークンだけでは使用できない。 送信者制約付きトークンが窃取されても、攻撃者はそのトークンを直接使用できず、 秘密鍵も窃取する必要がある。要するに、 送信者制約付きトークンを使用すると、安全に保存する対象がトークンから 秘密鍵へ移るとも言える。理想的には、アプリケーションは、 [W3C.WebCryptoAPI]で生成したものなど、エクスポート不可の秘密鍵を使用するべきである。ブラウザ ストレージ内の暗号化されていないトークンがエクスポート不可の秘密鍵で保護されていれば、XSS 攻撃では 鍵を抽出できないため、攻撃者はトークンを使用できない。¶
アプリケーションがエクスポート不可の 鍵を生成する API を使用できない場合、アプリケーションは自身の実行 コンテキストから秘密鍵を分離するための措置を講じるべきである。そのための手法は、 セクション 8で説明した安全なトークンストレージメカニズムの使用と 類似している。¶
エクスポート不可の鍵は JavaScript コンテキスト内からの 外部流出に対して保護されるが、基盤となる秘密鍵をファイルシステムから持ち出すことは、依然として 潜在的な攻撃経路である。本書執筆時点では、[W3C.WebCryptoAPI]は、 エクスポート不可の鍵が実際に Trusted Platform Module(TPM)によって保護されることや、 ディスク上で暗号化された形式で保存されることを保証していない。攻撃者がユーザーのマシンの ファイルシステムに、たとえばマルウェアを介してアクセスできる場合、基盤となるファイルシステムから エクスポート不可の鍵を持ち出せる可能性は依然としてある。これは事実上、潜在的な攻撃経路を セッションハイジャック攻撃と同等にする。¶
認可サーバー混同攻撃は、 少なくとも 2 つの認可サーバーを サポートするすべてのクライアントにとって深刻な脅威となる。セクション 4.4( [RFC9700])では、混同攻撃と 前述した対策について さらに詳しく説明している。¶
Web のセキュリティメカニズムの多くは、3 要素
<scheme, hostname, port>として定義されるオリジンに依存する。たとえば、ブラウザは
異なるオリジンを持つブラウジングコンテキストを自動的に分離し、リソースを特定のオリジンに制限し、
送信されるクロスオリジンリクエストに CORS 制限を適用する。¶
したがって、単一のオリジンに複数の アプリケーションをデプロイしないことがベストプラクティスとされる。1 つの オリジンに 1 つのアプリケーションだけをデプロイするアーキテクチャでは、これらのブラウザ制限を活用して アプリケーションのセキュリティを向上できる。 さらに、アプリケーションごとに単一のオリジンを使用すると、CORS、CSP などの セキュリティ対策を設定およびデプロイしやすくなる。¶
本文書には IANA に対するアクションはない。¶
著者らは、William DennissおよびJohn Bradleyの成果に謝意を表する。 両氏によるネイティブアプリケーション向けの推奨事項は、 ブラウザベースアプリケーションのベストプラクティスの多くに影響を与えた。著者らはまた、Hannes Tschofenig およびTorsten Lodderstedt、この BCP が最初に提案された Internet Identity Workshop 27 のセッションの参加者、ならびに最終仕様の形成に寄与する アイデア、フィードバック、文言を提供した次の方々にも感謝する。¶
Andy Barlow、Andy Newton、Annabelle Backman、Brian Campbell、Brock Allen、Christian Mainka、Damien Bowden、 Daniel Fett、Deb Cooley、Elar Lang、Emmanuel Gautier、Erik Kline、Éric Vyncke、Eva Sarafianou、 Filip Skokan、George Fletcher、Hannes Tschofenig、Janak Amarasena、 John Bradley、Joseph Heenan、 Justin Richer、Karl McGuinness、 Karsten Meyer zu Selhausen、Leo Tohill、Louis Jannett、 Marc Blanchet、Martin Thomson、Matthew Bocci、Mike Bishop、Mike Jones、Mohamed Boucadair、Orie Steele、Qin Wu、 Rifaat Shekh-Yusef、Roman Danyliw、 Sean Kelleher、Thomas Broyer、Thomas Fossati、Tomek Stojecki、 Torsten Lodderstedt、Vittorio Bertocci、Watson Ladd、William Duncan、およびYannick Majoros。¶