适用于 Web 开发者的网站隔离功能

Mathias Bynens
Mathias Bynens

桌面版 Chrome 67 提供了一项名为默认启用网站隔离的新功能。这个 这篇文章介绍了网站隔离的意义、它的必要性,以及 Web 开发者为何应该进行这种隔离 请注意这一点

什么是网站隔离?

在互联网上,人们可以观看猫咪视频、管理加密货币钱包等等 - 但您肯定不希望fluffycats.example能够访问您的珍贵加密货币!幸运的是, 由于 Same-Origin 优势,网站通常无法在浏览器内访问彼此的数据 政策。尽管如此,恶意网站仍有可能试图绕过此政策攻击其他网站,并且 有时,在强制执行同源政策的浏览器代码中会发现安全错误。通过 Chrome 团队会努力尽快修复此类错误。

网站隔离是 Chrome 的一项安全功能,它提供了一道额外的安全防线, 使此类攻击得不到成功它可确保始终将不同网站的网页 不同的进程,每个进程都在沙盒中运行,从而限制相应进程可执行的操作。 还会阻止相应进程接收来自其他网站的特定类型的敏感数据。作为 因此,利用网站隔离功能,恶意网站将更难使用推测性 从其他网站窃取数据的边信道攻击(如 Spectre)。当 Chrome 团队完成 即使攻击者的网页可以破坏某些 如何在自己的流程中

网站隔离功能可有效降低不受信任的网站访问或窃取信息的难度 来自您在其他网站上的账号。它可针对各种类型的 比如 近期的 Meltdown 和 Spectre 旁路攻击

如需详细了解网站隔离,请参阅 Google 安全博客上的文章

跨源读取屏蔽

即使所有跨网站网页都放入不同的进程中,网页仍可以合法地请求 一些跨网站子资源,例如图片和 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> 将敏感数据提交到 内存:

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

跨源读取拦截 (CORB) 是一项新的安全功能,旨在阻止 balance.json 不会根据其 MIME 类型进入渲染程序进程内存的内存。

我们来详细了解一下 CORB 的工作原理。网站可以向服务器请求两种类型的资源:

  1. 数据资源,例如 HTML、XML 或 JSON 文档
  2. 媒体资源,例如图片、JavaScript、CSS 或字体

网站可以通过如下方式从自己的来源或其他来源接收数据资源: 宽容的 CORS 标头,例如 Access-Control-Allow-Origin: *。另一方面,媒体资源可以包含在任何 (即使没有宽松的 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 MIME 类型和 XML 资源, XML MIME 类型)。
  • 使用 X-Content-Type-Options: nosniff 标头停用嗅探功能。如果没有此标头, Chrome 会进行快速内容分析,以确认类型是否正确,但由于 而是允许响应,以免阻塞 JavaScript 文件、 你最好自己坚定地做对的事。

有关详情,请参阅 “面向 Web 开发者的 CORB”文章深入的 CORB 解说

Web 开发者为何应关注网站隔离?

在大多数情况下,网站隔离是一项幕后的浏览器功能,不能直接 提供给 Web 开发者。例如,不需要学习任何已在网络上公开的新 API。一般来说, 无论运行网站隔离功能,还是不启用网站隔离功能,网页都应该无法区分这两者。

不过,此规则也有一些例外情况。启用网站隔离功能有一些微妙调整 可能会对您的网站造成影响。我们维护着 一系列已知的网站隔离问题 下面我们将详细说明最重要的一些。

全屏布局不再是同步的

使用网站隔离功能时,我们不再保证全页布局是同步的,因为 一个页面现在可能会分散在多个进程中。如果网页认为 布局更改会立即应用到页面上的所有帧。

例如,假设有一个名为 fluffykittens.example 的网站,该网站与 social-widget.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 像素。之后,“毛茸茸的猫”页面 将宽度更改为 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 处理程序可以运行 任意长的时间,之后才允许提交新导航。

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

在这种情况下,所有帧中的 unload 处理程序都非常可靠。

不过,即使没有网站隔离功能,一些主框架导航也是跨进程导航,这会影响 卸载处理程序行为。例如,如果您通过输入old.examplenew.example 地址栏中的网址,new.example 导航会在一个新进程中发生。卸载 old.example 及其子帧的处理程序会在后台的 old.example 进程中运行, 如果 new.example 页面未能显示,旧的卸载处理程序会被终止 在特定超时时间内完成。由于卸载处理程序可能无法在超时前完成, 卸载行为的可靠性会降低。

有了网站隔离功能,所有跨网站导航都会成为跨进程操作, 不同的网站不会共享同一进程因此,上述情况适用于 更多用例,并且 <iframe> 中的卸载处理程序通常具有后台和超时行为 。

网站隔离导致的另一个差异是卸载处理程序的新并行排序方式: 如果不使用网站隔离,卸载处理程序会按照严格的自上而下顺序跨框架运行。但对于网站 隔离、卸载处理程序在不同进程之间并行运行。

这些是启用网站隔离功能的根本后果。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, 纳斯科·奥斯科夫 Philip Walton, Shubhie Panicker 和 托马斯·施泰纳 ,欢迎阅读本文的草稿版本并提供反馈。