为什么需要跨域隔离才能使用功能强大的功能,例如 SharedArrayBufferperformance.measureMemory() 和 JS Self-Profiling API。

简介

本文解释了为什么需要跨域隔离才能启用浏览器上的强大功能。

img

关键术语:本文使用了许多相似的术语。为了使事情更清楚,先让我们明确它们:

背景

Web 是基于 same-origin policy 构建的:这是一种安全功能,它是用来限制文档和脚本如何与其他来源的资源进行交互的。该原则限制了网站访问跨域资源的方式。例如,来自 https://a.example 的文档被禁止访问 https://b.example 上托管的数据。

但是,在历史上同源政策有一些例外。任何网站都可以:

  • 嵌入跨域iframe
  • 包含跨域资源,例如图像或脚本
  • 用 DOM 引用打开跨域弹出窗口

如果可以从头开始设计 Web,则这些异常将不存在。不幸的是,当 web 社区意识到严格的同源策略的优势时,web 已经开始依赖这些例外。

这种松散的同源策略的安全副作用有两种方式进行修补。一种方法是通过引入称为跨域资源共享(CORS)的新协议,其目的是确保服务器允许共享具有给定来源的资源。另一种方法是通过隐式删除对跨域资源的直接脚本访问,同时保留向后兼容性。这样的跨域资源称为“不透明”资源。这就是为什么除非把 CORS 应用于图像,否则通过CanvasRenderingContext2D 操作跨域图像的像素会失败的原因。

所有这些策略决策都在同一个浏览上下文组中发生(browsing context group)。

Browsing Context Group

长期以来,CORS 和不透明资源的结合就足够使浏览器安全了。尽管有时会发现一些极端情况(例如 JSON 漏洞)需要打补丁,但总的来说,不允许直接读取跨域资源的原始字节的原则是成功的。

所有这些都通过 Spectre 进行了更改,这使得加载到与代码同一的浏览上下文组中的任何数据都具有可读性。如果 evil.com 嵌入了跨域图像,则他们可以用 Spectre 攻击读取其像素数据,这使得依赖“不透明性”的保护无效。

Spectr

在理想情况下,所有跨域请求都应由拥有资源的服务器明确审核。如果拥有资源的服务器未提供审查,则数据将永远不会进入攻击者的浏览上下文组,因此他们将不会收到 Spectre 攻击的影响。我们称其为跨域隔离状态。这正是COOP + COEP的意义所在。

在跨域隔离状态下,发出请求的站点被认为不太危险,并且可以解锁强大的功能,例如 SharedArrayBufferperformance.measureMemory 和 JS Self-Profiling API,这些功能原本可以用于类似 Spectre 的攻击。它还防止修改 document.domain

跨域嵌入策略

跨域嵌入策略(COEP)阻止文档加载任何未明确授予文档许可权的跨域资源(使用CORP或CORS)。使用这个功能,你可以声明文档无法加载此类资源。

COEP 的工作原理

要激活此策略,需要添加以下 HTTP 头:

Cross-Origin-Embedder-Policy: require-corp

require-corp 关键字是 COEP 唯一接受的值。这将强制执行以下策略:文档只能从同一来源加载资源,或者显式被标记为可从另一来源加载的资源。

为了从其他来源加载资源,需要支持跨域资源共享(CORS)或跨域资源策略(CORP)。

跨域资源共享

如果跨域资源支持跨域资源共享(CORS),则可以使用 crossorigin 属性将其加载到你的网页上,而不会被 COEP 阻止。

<img src="https://third-party.example.com/image.jpg" crossorigin>

如果这个图片资源带有 CORS 标头,应该用 crossorigin 属性,以便将通过使用 CORS 模式获取资源的请求。除非设置了 CORS 标头,否则将会阻止图像加载。

同样,你可以通过 fetch() 方法获取跨域数据,只要服务器使用正确的 HTTP 头进行响应,就不需要特殊处理。

跨域资源策略

跨域资源策略(CORP)最初是作为一种选项被加入的,可以防止你的资源被其他域加载。在 COEP 的上下文中,CORP 可以指定谁可以加载资源的策略。

Cross-Origin-Resource-Policy 标头有三个可能的值:

Cross-Origin-Resource-Policy: same-site

标记为 same-site 的资源只能从相同站点加载。

Cross-Origin-Resource-Policy: same-origin

标有 same-origin 的资源只能从相同的来源加载。

Cross-Origin-Resource-Policy: cross-origin

被标记为 cross-origin 的资源可以被任何网站加载。 (这个值 与 COEP 一起添加到了 CORP 规范中。)

添加 COEP 标头后,将无法用 service worker 来绕过限制。如果文档受到 COEP 标头的保护,则在响应进入文档过程之前或在进入控制文档的 service worker 之前,将遵守策略。

跨域开放者策略

跨域开放者策略(COOP)允许你通过将其他文档放在不同的浏览器上下文组中,确保将其与其他文档隔离开,这样它们就不能直接与顶层窗口进行交互。例如,如果带有 COOP 的文档打开一个弹出窗口,则其 window.opener 属性将为 null。同样,打开器引用的 .closed 属性将返回 true

COOP

Cross-Origin-Opener-Policy 标头有三个可能的值:

Cross-Origin-Opener-Policy: same-origin

标有 same-origin 的文档可以与标有 same-origin 的同源文件共享相同的浏览上下文组。

COOP

Cross-Origin-Opener-Policy: same-origin-allow-popups

带有 same-origin-allow-popups 的顶级文档保留了对未设置 COOP 或通过把 COOP 设置为 unsafe-none 而选择退出隔离的任何弹出窗口的引用。

COOP

Cross-Origin-Opener-Policy: unsafe-none

unsafe-none 是默认设置,并且允许将文档添加到 opener 的浏览上下文组中,除非 opener 本身的 COOP 为 same-originsame-origin-allow-popups

noopener 属性有与 COOP 相同的效果,不同之处在于它只能工作从 opener 那边开始。 (当第三方打开窗口时,你不能取消它的关联。)通过执行诸如 window.open(url, '_blank', 'noopener')<a target="_blank" rel="noopener">, 之类的操作来附加 noopener 时,你可以故意将你的窗口与打开的窗口解除关联。

尽管 noopener 可以用 COOP 代替,但是当你想在不支持 COOP 的浏览器中保护网站时,它仍然很有用。

总结

如果要确保访问诸如 SharedArrayBufferPerformance.measureMemory 或 JS Self-Profiling API 之类的强大功能,只需记住你的文档需要同时使用 COEP (值为 require-corp )和 REOP(值为 same-origin)。

如果两者都不存在,浏览器将无法保证足够的隔离度以安全地启用那些强大的功能。你可以通过检查self.crossOriginIsolated 是否返回 true 来确定页面的状况。

使用 COOP 和 COEP 使网站“跨域隔离” 一文中了解实现此步骤的步骤。

资源