Fetch Metadata でウェブ攻撃からリソースを保護

CSRF、XSSI、クロスオリジンの情報漏洩を防ぐ。

ウェブリソースの分離を重視すべき理由

多くのウェブ アプリケーションは、クロスサイト リクエスト フォージェリ(CSRF)、クロスサイト スクリプト インクルード(XSSI)、タイミング攻撃、クロスオリジンの情報漏洩、投機的実行サイドチャネル(Spectre)攻撃などのクロスオリジン攻撃に対して脆弱です。

メタデータ取得リクエスト ヘッダーを使用すると、強力な多層防御メカニズム(リソース分離ポリシー)をデプロイして、これらの一般的なクロスオリジン攻撃からアプリケーションを保護できます。

特定のウェブ アプリケーションで公開されるリソースは、そのアプリケーション自体によってのみ読み込まれ、他のウェブサイトには読み込まれないのが一般的です。このような場合、メタデータ取得リクエスト ヘッダーに基づくリソース分離ポリシーのデプロイはそれほど手間がかかりません。また、クロスサイト攻撃からアプリケーションを保護します。

ブラウザの互換性

メタデータ取得リクエスト ヘッダーは、すべての最新のブラウザ エンジンでサポートされています。

対応ブラウザ

  • Chrome: 76。 <ph type="x-smartling-placeholder">
  • Edge: 79。 <ph type="x-smartling-placeholder">
  • Firefox: 90。 <ph type="x-smartling-placeholder">
  • Safari: 16.4。 <ph type="x-smartling-placeholder">

ソース

背景

クロスサイト攻撃は、デフォルトでウェブが開かれており、アプリケーション サーバーが外部アプリケーションから発信される通信から自身を簡単に保護できないために発生しがちです。 典型的なクロスオリジン攻撃は、クロスサイト リクエスト フォージェリ(CSRF)です。これは、攻撃者が自分の管理下のサイトにユーザーを誘い込んでから、ユーザーがログインしているサーバーにフォームを送信するというものです。サーバーはリクエストが別のドメイン(クロスサイト)から発信されたものかどうか判別できず、ブラウザがクロスサイト リクエストに Cookie を自動的に添付するため、サーバーはユーザーに代わって攻撃者がリクエストしたアクションを実行します。

クロスサイト・スクリプト・インクルージョン(XSSI)やクロスオリジンの情報漏洩など、その他のクロスサイト攻撃は、CSRF とよく似ており、標的のアプリケーションから攻撃者の管理下にあるドキュメントにリソースを読み込ませ、標的のアプリケーションに関する情報を漏洩させることに依存しています。アプリケーションは、信頼できるリクエストと信頼できないリクエストを簡単に区別できないため、悪意のあるクロスサイト トラフィックを破棄できません。

メタデータ取得の概要

メタデータ取得リクエスト ヘッダーは、サーバーをクロスオリジン攻撃から守るために設計された新しいウェブ プラットフォームのセキュリティ機能です。HTTP リクエストのコンテキストに関する情報を一連の Sec-Fetch-* ヘッダーで指定することで、応答側のサーバーはリクエストを処理する前にセキュリティ ポリシーを適用できるようになります。これにより、デベロッパーはリクエストがどのように行われたか、またリクエストが使用されるコンテキストに基づいて、それを承認するか拒否するかを決定できます。これにより、自身のアプリケーションによって行われた正当なリクエストのみに応答できます。

同一オリジン
<ph type="x-smartling-placeholder"></ph> 自社サーバーで処理している(同一オリジン)サイトからのリクエストは引き続き機能します。 JavaScript でリソース https://site.example/foo.json に対して https://site.example からフェッチ リクエストを行うと、ブラウザから HTTP リクエスト ヘッダー「Sec Fetch-Site: same-origin」が送信されます。
クロスサイト
<ph type="x-smartling-placeholder"></ph> 悪意のあるクロスサイト リクエストは、Sec-Fetch-* ヘッダーによって提供される HTTP リクエストに追加のコンテキストがあるため、サーバーが拒否する可能性があります。 img 要素の src 属性が「https://site.example/foo.json」に設定されている https://evil.example の画像ブラウザは、HTTP リクエスト ヘッダー「Sec-Fetch-Site: cross-site」を送信します。

Sec-Fetch-Site

対応ブラウザ

  • Chrome: 76。 <ph type="x-smartling-placeholder">
  • Edge: 79。 <ph type="x-smartling-placeholder">
  • Firefox: 90。 <ph type="x-smartling-placeholder">
  • Safari: 16.4。 <ph type="x-smartling-placeholder">

ソース

Sec-Fetch-Site は、リクエストを送信したサイトをサーバーに伝えます。ブラウザでは、この値は次のいずれかに設定されます。

  • same-origin: リクエストが独自のアプリケーション(site.example など)から送信された場合
  • same-site: リクエストがサイトのサブドメイン(bar.site.example など)から行われた場合
  • none: リクエストがユーザー エージェントとのやり取り(ブックマークのクリックなど)によって明示的に発生した場合
  • cross-site: リクエストが別のウェブサイト(evil.example など)から送信された場合

Sec-Fetch-Mode

対応ブラウザ

  • Chrome: 76。 <ph type="x-smartling-placeholder">
  • Edge: 79。 <ph type="x-smartling-placeholder">
  • Firefox: 90。 <ph type="x-smartling-placeholder">
  • Safari: 16.4。 <ph type="x-smartling-placeholder">

ソース

Sec-Fetch-Mode はリクエストのモードを示します。これはリクエストの種類にほぼ対応しており、リソースの読み込みとナビゲーション リクエストを区別できるようにします。たとえば、デスティネーションが navigate の場合はトップレベル ナビゲーション リクエストを示し、no-cors は画像の読み込みなどのリソース リクエストを示します。

Sec-Fetch-Dest

対応ブラウザ

  • Chrome: 80。 <ph type="x-smartling-placeholder">
  • Edge: 80。 <ph type="x-smartling-placeholder">
  • Firefox: 90。 <ph type="x-smartling-placeholder">
  • Safari: 16.4。 <ph type="x-smartling-placeholder">

ソース

Sec-Fetch-Dest はリクエストの宛先を公開します(たとえば、script タグまたは img タグによってブラウザからリソースがリクエストされた場合)。

メタデータ取得を使用してクロスオリジン攻撃から保護する方法

これらのリクエスト ヘッダーが提供する追加情報は非常にシンプルですが、追加のコンテキストにより、わずか数行のコードで強力なセキュリティ ロジック(リソース分離ポリシーとも呼ばれる)をサーバーサイドで構築できます。

リソース分離ポリシーの実装

リソース分離ポリシーは、リソースが外部ウェブサイトからリクエストされるのを防ぎます。このようなトラフィックをブロックすると、CSRF、XSSI、タイミング攻撃、クロスオリジンの情報漏洩など、クロスサイト ウェブの一般的な脆弱性を軽減できます。このポリシーは、アプリケーションのすべてのエンドポイントに対して有効にすることができ、独自のアプリケーションから送信されるすべてのリソース リクエストだけでなく、(HTTP GET リクエストを介した)直接移動も許可します。クロスサイト コンテキストで読み込まれるエンドポイント(CORS を使用して読み込まれるエンドポイントなど)は、このロジックをオプトアウトできます。

ステップ 1: メタデータ取得を送信しないブラウザからのリクエストを許可する

すべてのブラウザがメタデータの取得をサポートしているわけではないため、sec-fetch-site の有無を確認して、Sec-Fetch-* ヘッダーを設定していないリクエストを許可する必要があります。

if not req['sec-fetch-site']:
  return True  # Allow this request

ステップ 2: 同一サイトとブラウザから開始されたリクエストを許可する

クロスオリジン コンテキスト(evil.example など)以外で発生したリクエストはすべて許可されます。具体的には、次のようなリクエストがあります。

  • 独自のアプリケーション(site.example リクエスト site.example/foo.json が常に許可される同一オリジン リクエストなど)から作成されている。
  • 自分のサブドメインがベース。
  • ユーザーとユーザー エージェントとのやり取り(直接移動やブックマークのクリックなど)によって明示的に発生する。
if req['sec-fetch-site'] in ('same-origin', 'same-site', 'none'):
  return True  # Allow this request

ステップ 3: シンプルなトップレベル ナビゲーションと iframe 設定を許可する

サイトを他のサイトからリンクできるようにするには、シンプルな(HTTP GET)トップレベル ナビゲーションを許可する必要があります。

if req['sec-fetch-mode'] == 'navigate' and req.method == 'GET'
  # <object> and <embed> send navigation requests, which we disallow.
  and req['sec-fetch-dest'] not in ('object', 'embed'):
    return True  # Allow this request

ステップ 4: クロスサイト トラフィックを処理するエンドポイントをオプトアウトする(省略可)

アプリケーションによっては、クロスサイトで読み込むことを想定したリソースを提供することもあります。これらのリソースは、パスごとまたはエンドポイントごとに除外する必要があります。このようなエンドポイントの例を以下に示します。

  • クロスオリジンでアクセスされるエンドポイント: CORS 対応のエンドポイントをアプリケーションが提供している場合、これらのエンドポイントへのクロスサイト リクエストが引き続き可能になるように、それらのエンドポイントをリソース分離から明示的にオプトアウトする必要があります。
  • 公開リソース(画像、スタイルなど):他のサイトからのクロスオリジンで読み込み可能でなければならない公開リソースと未認証リソースも、除外できます。
if req.path in ('/my_CORS_endpoint', '/favicon.png'):
  return True

ステップ 5: クロスサイト リクエストであり、ナビゲーション リクエストではない他のすべてのリクエストを拒否する

その他のクロスサイト リクエストは、このリソース分離ポリシーによって拒否されるため、一般的なクロスサイト攻撃からアプリケーションを保護します。

例: 次のコードは、サーバー上またはミドルウェアとして堅牢なリソース分離ポリシーの完全な実装を示しています。悪意のある可能性があるクロスサイト リソース リクエストを拒否し、簡単なナビゲーション リクエストは許可します。

# Reject cross-origin requests to protect from CSRF, XSSI, and other bugs
def allow_request(req):
  # Allow requests from browsers which don't send Fetch Metadata
  if not req['sec-fetch-site']:
    return True

  # Allow same-site and browser-initiated requests
  if req['sec-fetch-site'] in ('same-origin', 'same-site', 'none'):
    return True

  # Allow simple top-level navigations except <object> and <embed>
  if req['sec-fetch-mode'] == 'navigate' and req.method == 'GET'
    and req['sec-fetch-dest'] not in ('object', 'embed'):
      return True

  # [OPTIONAL] Exempt paths/endpoints meant to be served cross-origin.
  if req.path in ('/my_CORS_endpoint', '/favicon.png'):
    return True

  # Reject all other requests that are cross-site and not navigational
  return False

リソース分離ポリシーのデプロイ

  1. 上記のコード スニペットのようなモジュールをインストールして、サイトの動作をログに記録してモニタリングし、制限が正当なトラフィックに影響を与えないようにします。
  2. 正規のクロスオリジン エンドポイントを除外して、潜在的な違反を修正します。
  3. 非遵守のリクエストを破棄してポリシーを適用する。

ポリシー違反を特定して修正する

まずサーバーサイドのコードでレポートモードでポリシーを有効にして、副作用のない方法でポリシーをテストすることをおすすめします。このロジックをミドルウェアやリバース プロキシに実装することもできます。リバース プロキシは、本番環境のトラフィックに適用した場合に、ポリシーで発生する可能性のある違反をログに記録します。

Google でメタデータ取得リソース分離ポリシーをロールアウトしてきた経験から、ほとんどのアプリケーションはデフォルトでこうしたポリシーに対応しており、クロスサイト トラフィックを許可するためにエンドポイントを除外することはほとんどありません。

リソース分離ポリシーの適用

ポリシーが本番環境の正規トラフィックに影響を与えないことを確認したら、制限を適用します。他のサイトからリソースをリクエストできないようにして、クロスサイト攻撃からユーザーを保護します。

関連情報