ウェブ デベロッパー向けのサイト分離

デスクトップ版 Chrome 67 には、サイト分離という新しい機能がデフォルトで有効になっています。この記事では、サイト分離の概要、必要性、ウェブ デベロッパーが認識しておくべき理由について説明します。

サイト分離とは何ですか?

インターネットは、猫の動画の視聴や暗号通貨ウォレットの管理などのために使用しますが、fluffycats.example に貴重な暗号通貨にアクセスしてほしくはありません。幸い、通常、同一オリジン ポリシーにより、ウェブサイトはブラウザ内で相互のデータにアクセスできません。それでも、悪意のあるウェブサイトがこのポリシーを回避して他のウェブサイトを攻撃しようとすることがあります。また、同一生成元ポリシーを適用するブラウザ コードにセキュリティ バグが見つかることもあります。Chrome チームは、このようなバグをできるだけ早く修正することを目指しています。

サイト分離は、このような攻撃が成功しにくいように、追加の防御ラインを提供する Chrome のセキュリティ機能です。各ウェブサイトのページが常に別々のプロセスで処理されるようになり、各プロセスは、実行可能な処理が制限されるサンドボックス内で実行されます。また、プロセスが他のサイトから特定のタイプのセンシティブ データを受信しないようにします。そのため、サイト分離では、悪意のあるウェブサイトが Spectre などの推測サイドチャネル攻撃を使用して他のサイトからデータを盗むことははるかに難しくなります。Chrome チームが追加の適用を完了すると、攻撃者のページが独自のプロセスで一部のルールを破った場合でも、サイト分離が役立ちます。

サイト分離により、信頼できないウェブサイトが他のウェブサイトのアカウントにアクセスしたり、情報を盗み出したりすることを効果的に困難にします。これにより、最近の Meltdown と Spectre のサイドチャネル攻撃など、さまざまな種類のセキュリティ バグに対する保護が強化されます。

Site Isolation の詳細については、Google セキュリティ ブログの記事をご覧ください。

Cross-Origin Read Blocking

すべてのクロスサイト ページが個別のプロセスに配置されている場合でも、ページは画像や JavaScript などの一部のクロスサイト サブリソースを正当にリクエストできます。悪意のあるウェブページは、<img> 要素を使用して、銀行残高などの機密データを含む JSON ファイルを読み込む可能性があります。

<img src="https://your-bank.example/balance.json" />
<!-- Note: the attacker refused to add an `alt` attribute, for extra evil points. -->

サイト分離がないと、JSON ファイルの内容がレンダラ プロセスのメモリに保存されます。この時点で、レンダラは有効な画像形式ではないことを認識し、画像をレンダリングしません。しかし、攻撃者は Spectre などの脆弱性を悪用して、そのメモリ チャンクを読み取る可能性があります。

攻撃者は、<img> ではなく <script> を使用して機密データをメモリに commit することもできます。

<script src="https://your-bank.example/balance.json"></script>

Cross-Origin Read Blocking(CORB)は、MIME タイプに基づいて balance.json の内容がレンダラ プロセスのメモリに侵入するのを防ぐ新しいセキュリティ機能です。

CORB の仕組みを詳しく見てみましょう。ウェブサイトは、サーバーから次の 2 種類のリソースをリクエストできます。

  1. HTML、XML、JSON ドキュメントなどのデータリソース
  2. 画像、JavaScript、CSS、フォントなどのメディア リソース

ウェブサイトは、Access-Control-Allow-Origin: * などの許可付き CORS ヘッダーを使用して、自身のオリジンまたは他のオリジンからデータリソースを受け取ることができます。一方、メディア リソースは、許可付きの CORS ヘッダーがなくても、任意のオリジンから含めることができます。

CORB は、次の場合に、レンダラ プロセスがクロスオリジン データリソース(HTML、XML、JSON など)を受信しないようにします。

  • リソースに X-Content-Type-Options: nosniff ヘッダーがある
  • CORS でリソースへのアクセスが明示的に許可されていない

クロスオリジン データリソースに X-Content-Type-Options: nosniff ヘッダーが設定されていない場合、CORB はレスポンスの本文をスニッフィングして、HTML、XML、JSON のいずれであるかを判断しようとします。これは、一部のウェブサーバーが構成ミスにより、画像を text/html として提供することがあるため必要です。

CORB ポリシーによってブロックされたデータリソースは、空としてプロセスに提示されますが、リクエストはバックグラウンドで引き続き行われます。その結果、悪意のあるウェブページがクロスサイト データをプロセスに引き込んで盗み出すのは困難になります。

最適なセキュリティを確保し、CORB のメリットを活用するには、次のことをおすすめします。

  • レスポンスに正しい Content-Type ヘッダーを付けます。(たとえば、HTML リソースは text/html として、JSON リソースは JSON MIME タイプで、XML リソースは XML MIME タイプで提供する必要があります)。
  • X-Content-Type-Options: nosniff ヘッダーを使用して、スニッフィングをオプトアウトします。このヘッダーがない場合、Chrome は簡単なコンテンツ分析を行い、タイプが正しいことを確認しようとしますが、JavaScript ファイルなどのブロックを回避するためにレスポンスの許可側にエラーが発生するため、自分で正しいことを行うことをおすすめします。

詳しくは、ウェブ デベロッパー向けの CORB に関する記事またはCORB の詳細な説明をご覧ください。

ウェブ デベロッパーがサイト分離に注目すべき理由

ほとんどの場合、サイト分離は、ウェブ デベロッパーに直接公開されない、ブラウザの裏側の機能です。たとえば、学習する新しいウェブ公開 API はありません。一般に、ウェブページは、サイト分離ありの場合とサイト分離なしの場合で違いを認識できません。

ただし、このルールには例外があります。サイト分離を有効にすると、ウェブサイトに影響する可能性のある微妙な副作用がいくつか発生します。Google はサイト分離に関する既知の問題のリストを維持しています。以下では、最も重要な問題について詳しく説明します。

全ページレイアウトが非同期になりました

サイト分離では、ページのフレームが複数のプロセスに分散される可能性があるため、全ページのレイアウトが同期されるとは限りません。レイアウトの変更がページ上のすべてのフレームにすぐに伝播すると想定しているページは、影響を受ける可能性があります。

たとえば、social-widget.example でホストされているソーシャル ウィジェットと通信する fluffykittens.example という名前のウェブサイトについて考えてみましょう。

<!-- https://fluffykittens.example/ -->
<iframe src="https://social-widget.example/" width="123"></iframe>
<script>
  const iframe = document.querySelector('iframe');
  iframe.width = 456;
  iframe.contentWindow.postMessage(
    // The message to send:
    'Meow!',
    // The target origin:
    'https://social-widget.example'
  );
</script>

最初、ソーシャル ウィジェットの <iframe> の幅は 123 ピクセルです。その後、FluffyKittens ページで幅が 456 ピクセルに変更され(レイアウトがトリガーされます)、ソーシャル ウィジェットにメッセージが送信されます。このウィジェットのコードは次のとおりです。

<!-- https://social-widget.example/ -->
<script>
  self.onmessage = () => {
    console.log(document.documentElement.clientWidth);
  };
</script>

ソーシャル ウィジェットが postMessage API を介してメッセージを受信するたびに、ルート <html> 要素の幅がログに記録されます。

どの幅の値がログに記録されますか?Chrome でサイト分離が有効になる前は、答えは 456 でした。document.documentElement.clientWidth にアクセスするとレイアウトが強制されます。これは、Chrome でサイト分離が有効になる前は同期的でした。ただし、サイト分離を有効にすると、クロスオリジンのソーシャル ウィジェットの再レイアウトが、別のプロセスで非同期的に行われるようになりました。そのため、答えは 123(古い width 値)にもできます。

ページがクロスオリジン <iframe> のサイズを変更してから postMessage を送信する場合、サイト分離では、受信フレームがメッセージを受信したときに新しいサイズをまだ認識していない可能性があります。より一般的には、レイアウト変更がページ上のすべてのフレームにすぐに伝播すると想定すると、ページが破損する可能性があります。

この特定の例では、より堅牢なソリューションとして、親フレームに width を設定し、resize イベントをリッスンして <iframe> の変更を検出します。

アンロード ハンドラが頻繁にタイムアウトする

フレームが移動または閉じると、古いドキュメントとそれに埋め込まれたサブフレーム ドキュメントがすべて unload ハンドラを実行します。新しいナビゲーションが同じレンダラ プロセスで発生する場合(同じオリジンのナビゲーションの場合など)、古いドキュメントとそのサブフレームの unload ハンドラは、新しいナビゲーションを commit する前に任意の長い時間実行される可能性があります。

addEventListener('unload', () => {
  doSomethingThatMightTakeALongTime();
});

この場合、すべてのフレームの unload ハンドラは非常に信頼性が高いです。

ただし、サイト分離がない場合でも、一部のメインフレーム ナビゲーションはクロスプロセスであり、アンロード ハンドラの動作に影響します。たとえば、アドレスバーに URL を入力して old.example から new.example に移動すると、new.example へのナビゲーションは新しいプロセスで実行されます。old.example とそのサブフレームのアンロード ハンドラは、new.example ページが表示された後、バックグラウンドで old.example プロセスで実行されます。古いアンロード ハンドラは、特定のタイムアウト内に完了しなかった場合に終了します。タイムアウト前にアンロード ハンドラが完了しない可能性があるため、アンロードの動作の信頼性が低下します。

サイト分離では、すべてのクロスサイト ナビゲーションがクロスプロセスになるため、異なるサイトのドキュメントがプロセスを共有することはありません。その結果、上記の状況がより多くのケースに適用され、<iframe> のアンロード ハンドラには、上記のバックグラウンドとタイムアウトの動作がよく見られます。

サイト分離によるもう 1 つの違いは、アンロード ハンドラの新しい並列順序です。サイト分離がないと、アンロード ハンドラはフレーム全体で厳密なトップダウン順序で実行されます。ただし、サイト分離では、アンロード ハンドラは異なるプロセス間で並行して実行されます。

これらは、サイト分離を有効にした場合の基本的な結果です。Chrome チームは、可能な限り、一般的なユースケースでアンロード ハンドラの信頼性を向上させる取り組みを進めています。また、サブフレーム アンロード ハンドラが特定の機能をまだ利用できないバグがあることも認識しており、解決に向けて取り組んでいます。

アンロード ハンドラの重要なケースは、セッション終了時の ping を送信することです。通常は次のように行います。

addEventListener('pagehide', () => {
  const image = new Image();
  img.src = '/end-of-session';
});

この変更に照らし合わせてより堅牢なアプローチとしては、代わりに navigator.sendBeacon を使用する方法があります。

addEventListener('pagehide', () => {
  navigator.sendBeacon('/end-of-session');
});

リクエストをより細かく制御する必要がある場合は、Fetch API の keepalive オプションを使用できます。

addEventListener('pagehide', () => {
  fetch('/end-of-session', {keepalive: true});
});

まとめ

サイト分離では、各サイトを独自のプロセスに分離することで、信頼できないウェブサイトが他のウェブサイトのアカウントにアクセスしたり、情報を盗み出したりすることを困難にします。その一環として、CORB は機密データ リソースをレンダラ プロセスから除外しようとします。上記の推奨事項に沿って設定することで、これらの新しいセキュリティ機能を最大限に活用できます。

この記事の下書き版を読んでフィードバックをくれた Alex Moshchuk、Charlie Reis、Jason Miller、Nasko Oskov、Philip Walton、Shubhie Panicker、Thomas Steiner に感謝します。