防止 CSRF、XSSI 和跨源信息泄露。
为什么您应该关心隔离 Web 资源?
许多 Web 应用都容易受到跨源攻击,例如跨站请求伪造 (CSRF)、跨站脚本包含 (XSSI)、时间攻击、跨源信息泄露或推测执行侧信道 (Spectre) 攻击。
借助提取元数据请求标头,您可以部署强大的纵深防御机制(即资源隔离政策),以保护您的应用免受这些常见的跨源攻击。
通常,给定 Web 应用公开的资源仅由该应用本身加载,而不会由其他网站加载。在这种情况下,您只需花费很少的精力即可根据提取元数据请求标头部署资源隔离政策,同时保护应用免受跨站攻击。
浏览器兼容性
所有现代浏览器引擎都支持提取元数据请求标头。
背景
由于网站默认处于开放状态,并且您的应用服务器无法轻松保护自己免受来自外部应用的通信攻击,因此可能会发生许多跨网站攻击。典型的跨源攻击是跨站请求伪造 (CSRF),攻击者诱骗用户访问其控制的网站,然后向用户登录的服务器提交表单。由于服务器无法确定请求是否来自其他网域(跨网站),并且浏览器会自动将 Cookie 附加到跨网站请求,因此服务器将代表用户执行攻击者请求的操作。
其他跨站攻击(例如跨站脚本包含 [XSSI] 或跨源信息泄露)的性质与 CSRF 类似,依赖于在攻击者控制的文档中加载来自受害应用的资源,并泄露受害应用的相关信息。由于应用无法轻松区分可信请求和不可信请求,因此无法丢弃恶意跨网站流量。
推出提取元数据
提取元数据请求标头是一项新的 Web 平台安全功能,旨在帮助服务器防范跨源攻击。通过在一组 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 步:允许简单的顶级导航和 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
部署资源隔离政策
- 安装上述代码段等模块,以记录和监控网站的行为,并确保限制不会影响任何合法流量。
- 通过豁免合法的跨源端点来解决潜在违规问题。
- 通过丢弃不合规的请求来强制执行该政策。
识别和解决违反政策的问题
建议您先在服务器端代码中以报告模式启用政策,以免产生副作用。或者,您也可以在中间件或反向代理中实现此逻辑,后者会记录您的政策应用于生产流量时可能产生的任何违规行为。
根据我们在 Google 部署提取元数据资源隔离政策的经验,大多数应用默认与此类政策兼容,并且很少需要豁免端点以允许跨网站流量。
强制执行资源隔离政策
检查您的政策不会影响合法生产流量后,您就可以强制执行限制了,这样可以确保其他网站无法请求您的资源,并保护您的用户免受跨网站攻击。