使用 Fetch Metadata 保护您的资源免受网络攻击

防止 CSRF、XSSI 和跨源信息泄露。

Lukas Weichselbaum
Lukas Weichselbaum

为什么应该关注隔离 Web 资源?

许多 Web 应用都容易受到跨源攻击,例如跨站请求伪造 (CSRF)、跨站脚本包含 (XSSI)、计时攻击、跨源信息泄露或推测执行边信道 (Spectre) 攻击。

借助提取元数据请求标头,您可以部署强大的深度防御机制(资源隔离政策),保护您的应用免受这些常见的跨源攻击。

给定 Web 应用所提供的资源只能由该应用本身加载,而不能由其他网站加载,这种情况很常见。在这种情况下,部署基于提取元数据请求标头的资源隔离政策毫不费力,同时还可以保护应用程序免受跨站攻击。

浏览器兼容性

所有现代浏览器引擎都支持提取元数据请求标头。

浏览器支持

  • Chrome:76。
  • Edge:79。
  • Firefox:90.
  • Safari:16.4。

来源

背景

由于网络默认处于开放状态,您的应用服务器无法轻松地保护自己免受源自外部应用的通信的侵扰,因此可能会发生许多跨网站攻击。 典型的跨源攻击是跨站请求伪造 (CSRF),即攻击者诱使用户访问他们控制的网站,然后将表单提交到用户登录的服务器。由于服务器无法分辨请求是否来自另一个网域(跨网站),并且浏览器会自动将 Cookie 附加到跨网站请求,因此服务器将代表用户执行攻击者请求的操作。

其他跨站攻击(例如跨站脚本包含 [XSSI] 或跨源信息泄露)的性质与 CSRF 类似,依赖于在攻击者控制的文档中加载来自受害应用的资源,并泄露受害应用的相关信息。由于应用无法轻松区分受信任的请求与不受信任的请求,因此无法舍弃恶意跨站点流量。

推出提取元数据

“提取元数据”请求标头是一项新的网络平台安全功能,旨在帮助服务器抵御跨源攻击。通过在一组 Sec-Fetch-* 标头中提供有关 HTTP 请求上下文的信息,它们允许响应服务器在处理请求之前应用安全政策。这样,开发者就可以根据请求的提交方式和使用情境来决定是否接受或拒绝请求,从而仅响应由自己的应用发出的合法请求。

同源
来自您自己的服务器提供的网站(同源)的请求将继续正常运行。 使用 JavaScript 从 https://site.example 提取资源 https://site.example/foo.json 的请求会导致浏览器发送 HTTP 请求标头“Sec Fetch-Site: same-origin”。
跨网站
由于 Sec-Fetch-* 标头在 HTTP 请求中提供了额外的上下文,因此服务器可能会拒绝恶意跨站请求。 如果 https://evil.example 上的图片将 img 元素的 src 属性设置为“https://site.example/foo.json”,则会导致浏览器发送 HTTP 请求标头“Sec-Fetch-Site: cross-site”。

Sec-Fetch-Site

浏览器支持

  • Chrome:76。
  • Edge:79。
  • Firefox:90.
  • Safari:16.4.

来源

Sec-Fetch-Site 会告知服务器发送请求的网站。浏览器将此值设置为以下某个值:

  • same-origin(如果请求是由您自己的应用发出的,例如 site.example
  • same-site,如果请求是由您网站的子网域(例如 bar.site.example)发出的
  • none,如果请求是由用户与用户代理的交互(例如点击书签)明确触发的
  • cross-site(如果请求是由其他网站(例如 evil.example)发送的)

Sec-Fetch-Mode

浏览器支持

  • Chrome:76。
  • Edge:79。
  • Firefox:90。
  • Safari:16.4。

来源

Sec-Fetch-Mode 表示请求的模式。这大致对应于请求的类型,使您能够区分资源加载和导航请求。例如,目标为 navigate 表示顶级导航请求,而 no-cors 表示加载图片等资源请求。

Sec-Fetch-Dest

浏览器支持

  • Chrome:80。
  • Edge:80.
  • Firefox:90。
  • Safari:16.4。

来源

Sec-Fetch-Dest 会公开请求的目标(例如,如果 scriptimg 标记导致浏览器请求资源)。

如何使用提取元数据来防范跨源攻击

这些请求标头提供的额外信息非常简单,但借助这些额外背景信息,您只需几行代码即可在服务器端构建强大的安全逻辑(也称为资源隔离政策)。

实现资源隔离政策

资源隔离政策可防止外部网站请求您的资源。屏蔽此类流量有助于防范常见的跨站 Web 漏洞,例如 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 步:允许简单的顶级导航和 iframing

为确保您的网站仍可从其他网站链接到您的网站,请务必启用简单的顶级导航 (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 部署提取元数据资源隔离政策的经验,大多数应用在默认情况下都与此类政策兼容,并且很少需要豁免端点以允许跨网站流量。

强制执行资源隔离政策

在确认自己的政策不会影响合法生产流量后,您就可以执行限制了,确保其他网站无法请求您的资源,并保护您的用户免受跨网站攻击。

深入阅读