通过获取元数据保护资源免受 Web 攻击

通过获取元数据保护资源免受 Web 攻击

通过获取元数据保护资源免受 Web 攻击

许多 Web 应用程序容易受到跨域攻击,例如跨站点请求伪造(CSRF),跨站点脚本包含(XSSI),定时攻击,跨域信息泄漏或推测性执行旁通道(Spectre)攻击。

通过获取元数据请求标头,你可以部署强大的深度防御机制(一种资源隔离策略),以保护你的应用程序免受这些常见的跨域攻击。

Web 应用程序公开的资源通常仅由应用程序本身而不由其他网站加载。在这种情况下,基于 “获取元数据” 请求标头部署资源隔离策略会花费很少的精力,同时可以保护应用程序免受跨站点攻击。

浏览器兼容性

Chrome 76 和其他基于 Chromium 的其他浏览器均支持获取元数据请求标头,Firefox 正在开发中。

背景

因为默认情况下 Web 是开放的,并且你的应用程序服务器无法轻易保护自己免受来自外部应用程序的通信,所以可能会发生许多跨站点攻击。典型的跨域攻击是跨站点请求伪造(CSRF),在这种攻击中,攻击者将用户引诱到他们控制的站点上,然后向用户登录的服务器提交表单。由于服务器无法确定请求是否源自另一个域(跨站点),并且浏览器会自动将 Cookie 附加到跨站点请求,因此服务器将代表用户执行攻击者请求的操作。

其他跨站点攻击,例如跨站点脚本包含(XSSI)或跨域信息泄漏,在本质上与 CSRF 相似,它们依赖于在攻击者控制的文档中从受害者应用程序加载资源并泄漏有关受害者应用程序的信息。由于应用程序无法轻松地区分受信任的请求和不受信任的请求,因此它们无法丢弃恶意的跨站点流量。

除了上述对资源的攻击之外,窗口引用还可能导致跨域信息泄漏和 Spectre 攻击。你可以通过将Cross-Origin-Opener-Policy响应标头设置为same-origin来阻止它们。

引入 Fetch 元数据

提取元数据请求标头是一项新的 Web 平台安全功能,旨在帮助服务器防御跨域攻击。通过在一组Sec-Fetch-*标头中提供有关 HTTP 请求的上下文的信息,它们允许响应服务器在处理请求之前应用安全策略。这使开发人员可以根据发出请求的方式以及使用该请求的上下文来决定是接受还是拒绝请求,从而可以仅响应其自己的应用程序发出的合法请求。

相同站点:

来自你自己的服务器(相同来源)所服务的站点的请求将继续起作用。

来自你自己的服务器(相同来源)所服务的站点的请求将继续起作用。

不同站点:

服务器可能会拒绝恶意的跨站点请求,因为Sec-Fetch-*标头提供的 HTTP 请求中存在其他上下文。

服务器可能会拒绝恶意的跨站点请求,因为 Sec-Fetch-* 标头提供的 HTTP 请求中存在其他上下文。

Sec-Fetch-Site

Sec-Fetch-Site告诉服务器哪个站点发送了请求。浏览器将此值设置为以下值之一:

  • same-origin,如果请求是由你自己的应用程序发出的(例如site.example
  • same-site,如果请求是由你网站的子域提出的(例如bar.site.example
  • none,如果请求是由用户与用户代理的交互(例如,单击书签)明确引起的
  • cross-site,如果请求是由另一个网站(例如evil.example)发送的

Sec-Fetch-Mode

Sec-Fetch-Mode指示模式的请求的。这大致对应于请求的类型,并允许你将资源负载与导航请求区分开。例如,目的地navigate表示顶级导航请求,而目的地则no-cors表示加载图像之类的资源请求。

Sec-Fetch-Dest

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

如何使用提取元数据,以防止跨域攻击

这些请求标头提供的额外信息非常简单,但是额外的上下文允许你仅用几行代码就可以在服务器端构建功能强大的安全逻辑,也称为资源隔离策略。

实现一个资源隔离策略

资源隔离策略可防止外部网站请求你的资源。阻止此类流量可缓解常见的跨站点 Web 漏洞,例如 CSRF,XSSI,定时攻击和跨域信息泄漏。可以为你的应用程序的所有端点启用此策略,并将允许所有来自你自己的应用程序的资源请求以及直接导航(通过 HTTPGET请求)。可以从该逻辑中选择应该在跨站点上下文中加载的端点(例如,使用 CORS 加载的端点)。

第 1 步:允许不发送获取元数据的浏览器的请求

由于并非所有浏览器都支持提取元数据,因此你需要Sec-Fetch-*通过检查是否存在来允许未设置标头的请求sec-fetch-site

以下示例都为 Python 代码

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

如果你的子域不受完全信任,则可以通过删除该same-site值来阻止来自子域的请求,从而使策略更加严格。

步骤 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

上面的逻辑可以防止你的应用程序的端点被其他网站用作资源,但是将允许顶层导航和嵌入(例如,加载到 <iframe> 中)。为了进一步提高安全性,可以使用 Fetch Metadata 标头将跨站点导航限制为仅允许的一组页面。

步骤 4:选择退出旨在提供跨站点流量的端点(可选)

在某些情况下,你的应用程序可能会提供旨在跨站点加载的资源。这些资源需要按每个路径或每个端点免除。此类端点的示例是:

  • 打算跨域访问的端点:如果你的应用程序服务于已CORS启用的端点,则需要明确地将其从资源隔离中退出,以确保仍然可以向这些端点进行跨站点请求。
  • 公共资源(例如图像,样式等):也应免除应从其他站点跨域加载的任何公共和未经身份验证的资源。
if req.path in ('/my_CORS_endpoint', '/favicon.png'):
  return True

警告: 在从这些安全限制中选择应用程序的某些部分之前,请确保它们是静态的并且不包含任何敏感的用户信息。

第 5 步:拒绝那些跨站点的所有其他请求,而不是导航

此资源隔离策略将拒绝任何其他跨站点请求,从而保护你的应用程序免受常见的跨站点攻击。

默认情况下,违反策略的请求应通过HTTP 403响应予以拒绝。但是,根据你的用例,你还可以考虑其他操作,例如:

  • 仅违反日志记录。在测试策略的兼容性并查找可能需要选择退出的端点时,这特别有用。
  • 修改请求。在某些情况下,请考虑执行其他操作,例如重定向到你的登录页面和删除身份验证凭据(例如 cookie)。但是,请注意,这可能会削弱基于 Fetch Metadata 的策略的保护。

示例:以下代码演示了在服务器上或作为中间件在拒绝简单的导航请求的同时拒绝潜在的恶意跨站点资源请求的可靠资源隔离策略的完整实现:

# 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 推出 “获取元数据资源隔离策略” 的经验,默认情况下,大多数应用程序都与该策略兼容,并且很少需要豁免端点才能允许跨站点流量。

强制执行资源隔离策略

在检查完你的策略不会影响合法的生产流量之后,你就可以执行限制了,以确保其他站点将无法请求你的资源并保护用户免受跨站点攻击。