防止 CSRF、XSSI 和跨源信息泄露。
为何应关注隔离 Web 资源?
许多 Web 应用都容易遭到跨源攻击,例如跨站请求伪造 (CSRF)、跨站脚本包含 (XSSI)、计时攻击、跨源信息泄露或推测执行旁路 (Spectre) 攻击。
借助 Fetch Metadata(提取元数据)请求标头,您可以部署强大的深度防御机制(即资源隔离政策),以保护您的应用免受这些常见的跨源攻击。
给定 Web 应用提供的资源通常仅由应用本身加载,而不能由其他网站加载。在这种情况下,根据提取元数据请求标头部署资源隔离政策只需极少的工作量,同时还能保护应用免受跨站攻击。
浏览器兼容性
所有现代浏览器引擎都支持提取元数据请求标头。
背景
由于 Web 默认是开放的,您的应用服务器无法轻易地保护自身免受来自外部应用的通信的侵害,因此存在许多跨网站攻击。典型的跨源攻击是跨站请求伪造 (CSRF),攻击者诱使用户访问其控制的网站,然后将表单提交给用户登录的服务器。由于服务器无法分辨请求是否来自其他域(跨网站),并且浏览器会自动将 Cookie 附加到跨网站请求,因此会代表用户执行攻击者请求的操作。
其他跨站攻击(例如跨站脚本包含 (XSSI) 或跨源信息泄露)在本质上与 CSRF 类似,并依赖于攻击者控制文档中从受害者应用加载资源,以及泄露受害者应用的相关信息。由于应用无法轻松区分可信请求与不可信请求,因此无法舍弃恶意跨站点流量。
提取元数据简介
“提取元数据”请求标头是一项全新的网络平台安全功能,旨在帮助服务器防范跨源攻击。通过在一组 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
公开请求的目标位置(例如,如果 script
或 img
标记导致浏览器请求某个资源),
如何使用提取元数据来防范跨源攻击
这些请求标头提供的额外信息非常简单,但是通过这些额外上下文,您只需几行代码即可在服务器端构建强大的安全逻辑(也称为资源隔离政策)。
实现资源隔离政策
资源隔离政策可防止外部网站请求您的资源。阻止此类流量可以缓解常见的跨网站 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 步:允许简单的顶级导航和 iframe
为确保仍能从其他网站链接到您的网站,您必须允许使用简单的 (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
部署资源隔离政策
- 安装一个类似上述代码段的模块,以记录和监控您网站的行为,并确保限制不会影响任何合法流量。
- 通过豁免合法跨源端点来解决潜在违规问题。
- 通过丢弃不合规的请求来强制执行此政策。
找出并修正违规问题
建议您先在服务器端代码的报告模式下启用政策,以便以没有任何附带效应的方式测试政策。或者,您也可以在中间件或反向代理中实现此逻辑,以记录您的政策在应用于生产流量时可能产生的任何违规行为。
根据 Google 发布提取元数据资源隔离政策的经验,大多数应用在默认情况下都与此类政策兼容,很少需要豁免端点以允许跨网站流量。
强制执行资源隔离政策
在确认您的政策不会影响合法的生产流量后,您便可以强制执行限制,以确保其他网站无法请求您的资源,并保护您的用户免受跨网站攻击。