プライベート ネットワーク アクセス: プリフライトの導入

更新

  • 2022 年 7 月 7 日: 現在のステータスを更新し、IP アドレス空間の定義を追加しました。
  • 2022 年 4 月 27 日: タイムラインの更新に関するお知らせ。
  • 2022 年 3 月 7 日: Chrome 98 で問題が見つかったため、ロールバックを発表しました。

はじめに

Chrome では、プライベート ネットワーク アクセス(PNA)仕様の一部として、公開ウェブサイトからプライベート ネットワーク エンドポイントへの直接アクセスを非推奨にします。

Chrome は、サブリソースに対するプライベート ネットワーク リクエストを送信する前に、CORS プリフライト リクエストを送信して、ターゲット サーバーからの明示的な許可を求めます。このプリフライト リクエストには新しいヘッダー Access-Control-Request-Private-Network: true が含まれ、そのレスポンスには対応するヘッダー Access-Control-Allow-Private-Network: true を含める必要があります。

これは、プライベート ネットワーク上のルーターやその他のデバイスをターゲットとするクロスサイト リクエスト フォージェリ(CSRF)攻撃からユーザーを保護することを目的としています。これらの攻撃は数十万人のユーザーに影響を与えています。攻撃者は、ユーザーを悪意のあるサーバーにリダイレクトしています。

リリース スケジュール

Chrome は、この変更をウェブサイトが認識してそれに応じた調整ができるように、2 段階に分けて変更を展開します。

  1. Chrome 104 の場合:

    • Chrome では、プライベート ネットワークのサブリソース リクエストの前にプリフライト リクエストを送信する試験運用版が実施されています。
    • プリフライトに失敗しても、DevTools に警告が表示されるだけです。プライベート ネットワーク リクエストには影響しません。
    • Chrome は互換性データを収集し、影響を受ける最大のウェブサイトに連絡します。
    • 既存のウェブサイトとの互換性は高いと見込まれます。
  2. 早ければ Chrome 113 で、以下の変更が適用されます。

    • 変更が十分に安全であると互換性データが示し、必要に応じて Google から直接連絡した場合にのみ、この処理が開始されます。
    • Chrome では、プリフライト リクエストが成功する必要があります。成功しなかった場合、リクエストは失敗します。
    • 同時にサポート終了トライアルが開始され、このフェーズの影響を受けるウェブサイトは猶予期間をリクエストできます。試用期間は 6 か月以上です。

プライベート ネットワーク アクセス(PNA)とは何ですか?

プライベート ネットワーク アクセス(旧称 CORS-RFC1918)は、プライベート ネットワーク上のサーバーにリクエストを送信するウェブサイトの機能を制限します。

Chrome では、仕様の一部がすでに実装されています。Chrome 96 以降、プライベート ネットワーク リクエストを送信できるのはセキュアなコンテキストのみです。詳しくは、以前のブログ投稿をご覧ください。

また、この仕様ではクロスオリジン リソース シェアリング(CORS)プロトコルが拡張され、ウェブサイトは任意のリクエストを送信する前に、プライベート ネットワーク上のサーバーに明示的に権限をリクエストする必要があります。

PNA が IP アドレスを分類してプライベート ネットワークを識別する仕組み

IP アドレスは、次の 3 つの IP アドレス空間に分類されます。 - public - private - local

ローカル IP アドレス空間には、RFC1122 のセクション 3.2.1.3 で定義された IPv4 ループバック アドレス(127.0.0.0/8)または RFC4291 のセクション 2.5.3 で定義された IPv6 ループバック アドレス(::1/128)のいずれかの IP アドレスが含まれます。

プライベート IP アドレス空間には、現在のネットワーク内でのみ意味を持つ IP アドレスが含まれています。これには、RFC1918 で定義された 10.0.0.0/8172.16.0.0/12192.168.0.0/16RFC3927 で定義されたリンクローカル アドレス 169.254.0.0/16RFC4193 で定義された一意のローカル IPv6 ユニキャスト アドレス fc00::/7RFC4291 のセクション 2.5.6 で定義されたリンクローカル IPv6 ユニキャスト アドレス fe80::/10、マッピングされた IPv4 アドレス自体がプライベートである IPv4 マッピングされた IPv6 アドレスが含まれます。

パブリック IP アドレス空間には、前述以外のすべてのアドレスが含まれます。

ローカル IP アドレスは、プライベート IP アドレスよりもプライベート性が高く、パブリック IP アドレスよりもプライベート性が高くなります。

より可用性の高いネットワークが、可用性の低いネットワークにリクエストを送信する場合、リクエストは非公開になります。
プライベート ネットワーク アクセス(CORS-RFC1918)のパブリック ネットワーク、プライベート ネットワーク、ローカル ネットワークの関係

詳細については、フィードバックを求めています: プライベート ネットワークの CORS(RFC1918)をご覧ください。

プリフライト リクエスト

背景

プリフライト リクエストは、クロスオリジン リソース シェアリング(CORS)標準で導入されたメカニズムです。副作用が生じる可能性がある HTTP リクエストを送信する前に、ターゲット ウェブサイトに権限をリクエストするために使用されます。これにより、ターゲット サーバーが CORS プロトコルを認識し、CSRF 攻撃のリスクを大幅に軽減できます。

権限リクエストは、今後の HTTP リクエストを記述する特定の CORS リクエスト ヘッダーとともに OPTIONS HTTP リクエストとして送信されます。レスポンスには、今後のリクエストに明示的に同意する特定の CORS レスポンス ヘッダーが含まれている必要があります。

CORS プリフライトを表すシーケンス図。OPTIONS HTTP リクエストがターゲットに送信され、200 OK が返されます。次に、CORS リクエスト ヘッダーが送信され、CORS レスポンス ヘッダーが返されます。

プライベート ネットワーク アクセスの新機能

プリフライト リクエストに、新しいリクエスト ヘッダーとレスポンス ヘッダーのペアが導入されました。

  • Access-Control-Request-Private-Network: true がすべての PNA プリフライト リクエストで設定されている
  • Access-Control-Allow-Private-Network: true は、すべての PNA プリフライト レスポンスで設定する必要があります

PNA のプリフライト リクエストは、リクエスト方法とモードに関係なく、すべてのプライベート ネットワーク リクエストに対して送信されます。cors モード、no-cors モード、その他のすべてのモードで、リクエストの前に送信されます。これは、リクエスト モードや、レスポンス コンテンツが開始側で利用可能かどうかに関係なく、すべてのプライベート ネットワーク リクエストが CSRF 攻撃に使用される可能性があるためです。

PNA のプリフライト リクエストは、ターゲット IP アドレスがイニシエータよりも非公開である場合、同じオリジンのリクエストにも送信されます。これは、プリフライト リクエストがクロスオリジン リクエストにのみ使用される通常の CORS とは異なります。同一オリジン リクエストのプリフライト リクエストは、DNS リバインディング攻撃を防ぎます。

検出可能な動作は、リクエストのモードによって異なります。

CORS なしモード

https://foo.example/index.html<img src="https://bar.example/cat.gif" alt="dancing cat"/> を埋め込み、bar.example192.168.1.1RFC 1918 に準拠したプライベート IP アドレス)に解決するとします。

Chrome はまずプリフライト リクエストを送信します。

HTTP/1.1 OPTIONS /cat.gif
Origin: https://foo.example
Access-Control-Request-Private-Network: true

このリクエストを成功させるには、サーバーが次のように応答する必要があります。

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://foo.example
Access-Control-Allow-Private-Network: true

その後、Chrome は実際のリクエストを送信します。

HTTP/1.1 GET /cat.gif
...

サーバーが通常どおり応答できるもの。

CORS モード

https://foo.example/index.html が次のコードを実行するとします。

await fetch('https://bar.example/delete-everything', {
  method: 'PUT',
  credentials: 'include',
})

ここでも、bar.example192.168.1.1 に解決されるとします。

Chrome はまずプリフライト リクエストを送信します。

HTTP/1.1 OPTIONS /delete-everything
Origin: https://foo.example
Access-Control-Request-Method: PUT
Access-Control-Request-Credentials: true
Access-Control-Request-Private-Network: true

このリクエストを成功させるには、サーバーが次のように応答する必要があります。

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://foo.example
Access-Control-Allow-Methods: PUT
Access-Control-Allow-Credentials: true
Access-Control-Allow-Private-Network: true

その後、Chrome は実際のリクエストを送信します。

HTTP/1.1 PUT /delete-everything
Origin: https://foo.example

通常の CORS ルールに従ってサーバーが応答できます。

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://foo.example

ウェブサイトが影響を受けているかどうかを確認する方法

Chrome 104 以降、プライベート ネットワーク リクエストが検出されると、その前にプリフライト リクエストが送信されます。このプリフライト リクエストが失敗した場合、最終リクエストは送信されますが、DevTools の問題パネルに警告が表示されます。

デベロッパー ツールの [問題] パネルに、プリフライト リクエストの失敗に関する警告が表示されている。つまり、限定公開ネットワーク リクエストは、許可されているリソースに対してのみ行われるようにし、特定のリクエストと影響を受けるリソースの詳細を記載します。

影響を受けるプリフライト リクエストは、ネットワーク パネルで確認して診断することもできます。

ローカルホストの DevTools ネットワーク パネルでプリフライト リクエストが失敗すると、ステータス 501 になります。

リクエストでプライベート ネットワーク アクセス ルールなしで通常の CORS プリフライトがトリガーされた場合、ネットワーク パネルに 2 つのプリフライトが表示され、最初のプリフライトは常に失敗したように表示されます。これは既知のバグであるため、無視してかまいません。

DevTools の [Network] パネルで、正常なプリフライトの前に、誤って失敗したプリフライト リクエストが表示されている。

プリフライトの成功が適用された場合にどうなるかを確認するには、Chrome 98 以降で次のコマンドライン 引数を渡すことができます。

--enable-features=PrivateNetworkAccessRespectPreflightResults

プリフライト リクエストが失敗すると、フェッチも失敗します。これにより、ロールアウト計画の第 2 フェーズ後にウェブサイトが機能するかどうかをテストできます。エラーは、上記の DevTools パネルを使用して警告と同じ方法で診断できます。

ウェブサイトが影響を受けている場合の対応

この変更が Chrome 104 でロールアウトされても、ウェブサイトが破損することはありません。ただし、ウェブサイトが想定どおりに動作し続けるように、影響を受けるリクエストパスを更新することを強くおすすめします。

次の 2 つの解決策があります。

  1. サーバーサイドでプリフライト リクエストを処理する
  2. エンタープライズ ポリシーで PNA チェックを無効にする

プリフライト リクエストをサーバーサイドで処理する

影響を受けるフェッチのターゲット サーバーを更新して、PNA プリフライト リクエストを処理します。まず、影響を受けるルートで標準の CORS プリフライト リクエストのサポートを実装します。次に、2 つの新しいレスポンス ヘッダーのサポートを追加します。

サーバーがプリフライト リクエスト(CORS ヘッダーを含む OPTIONS リクエスト)を受信すると、サーバーは Access-Control-Request-Private-Network: true ヘッダーの存在を確認する必要があります。このヘッダーがリクエストに存在する場合、サーバーは Origin ヘッダーとリクエストパスを、他の関連情報(Access-Control-Request-Headers など)とともに調べて、リクエストを安全に許可できることを確認する必要があります。通常は、管理対象の単一オリジンへのアクセスを許可する必要があります。

サーバーがリクエストを許可すると、必要な CORS ヘッダーと新しい PNA ヘッダーを使用して 204 No Content(または 200 OK)にレスポンスを返す必要があります。これらのヘッダーには、Access-Control-Allow-OriginAccess-Control-Allow-Private-Network: true のほか、必要に応じて他のものも含まれます。

具体的なシナリオについては、をご覧ください。

エンタープライズ ポリシーを使用してプライベート ネットワーク アクセスのチェックを無効にする

ユーザーを管理している場合は、次のいずれかのポリシーを使用して、プライベート ネットワーク アクセスのチェックを無効にできます。

詳しくは、Chrome ポリシー管理の概要をご覧ください。

ご意見をお寄せください

公開ネットワークからのリクエストを想定してプライベート ネットワーク内にウェブサイトをホストしている場合は、Chrome チームにフィードバックとユースケースをお知らせください。crbug.com で Chromium に関する問題を報告し、コンポーネントを Blink>SecurityFeature>CORS>PrivateNetworkAccess に設定して Google に報告してください。

次のステップ

今後、Chrome はプライベート ネットワーク アクセスのチェックを拡張し、ウェブワーカー(専用ワーカー、共有ワーカー、サービス ワーカー)を対象にします。警告の表示は、Chrome 107 で開始される予定です。

その後、Chrome はプライベート ネットワーク アクセスのチェックを拡張し、iframe やポップアップなどのナビゲーションを対象にします。警告の表示は、Chrome 108 で開始する予定です。

どちらの場合も、ウェブ デベロッパーが調整と互換性リスクの見積もりに時間をかけられるよう、同様の段階的なロールアウトを慎重に進めていきます。

謝辞

カバー写真: Mark OlsenUnsplash