获取元数据和隔离策略

了解有关获取元数据标头的所有信息,以及如何实施隔离策略来防御各种客户端攻击。

什么是获取元数据标头?

获取元数据标头是一种相对较新的浏览器安全功能,您可以在 Web 应用程序中使用它来防止前所未有的客户端攻击。

标题的目的很简单。它们告诉 Web 服务器发生 HTTP 请求的上下文。

一个例子

这是吉姆。b.example他使用 cookie登录SessionId=123

 假设有一个网站a.exampleb.example.

 

不获取元数据标头

在获取元数据标头存在之前,当 Jim 打开时,Web 服务器所看到的a.example只是有人使用 Jim 的 cookie 加载了图像。

 

在 HTTP 级别上,它可能如下所示:

GET /cat.jpg HTTP/1.1
Host: b.example
Cookie: SessionId=123

请注意,服务器无法知道是否a.example使用b.exampleJim 的会话 ID 加载图像。

使用获取元数据标头

在支持获取元数据标头的情况下,浏览器将在请求中向 Web 服务器发送更多信息。

 

在 HTTP 级别上,它可能如下所示:

GET /cat.jpg HTTP/1.1
Host: b.example
Cookie: SessionId=123
Sec-Fetch-Dest: image
Sec-Fetch-Mode: no-cors
Sec-Fetch-Site: cross-site

注意这次浏览器如何告诉网络服务器三个新的东西:

  1. 该请求源自图像元素。
  2. 请求的模式(这是一种资源负载,而不是例如导航)。
  3. 该请求来自另一个网站。

实际上,请求中还暗示了第四件事。缺少Sec-Fetch-User标头意味着请求不是由于用户交互而产生的。

获取元数据标头可以防止哪些攻击?

获取元数据标头可以帮助防止几乎任何跨站点攻击。这些包括:

获取元数据标头

该系统由四个 HTTP 请求标头组成。

Sec-Fetch-Site

Sec-Fetch-Site头告诉网络服务器请求是来自同一来源、同一站点还是完全不同的网站。它可以具有以下值之一:

  • same-origin:请求来自同一个origin,即hostportscheme相同。你可以在这里阅读更多关于起源的含义。
  • same-site: 请求来自不同的来源,但来自同一站点,这是同一可注册域的子域的浏览器术语。例如:https://a.example.com并且https://b.example.com被认为是同一站点(但来源不同)。
  • cross-site: 请求完全来自不同的网站。例如,www.google.com并且www.facebook.com被认为是跨站点的。
  • none: 请求并非来自任何网站。当用户打开书签、在 URL 栏中手动键入、打开另一个程序的链接等时,就会发生这种情况。

秒取模式

Sec-Fetch-Mode告诉网络服务器请求的模式。它可以具有以下值之一:

  • same-origin:浏览器正在向同一来源发出请求。如果目标来自不同的来源,使用此模式的请求将失败。
  • no-cors:浏览器正在向另一个来源发出请求,但不希望读取响应或使用任何未列入安全列表的 HTTP 动词或标头。
  • cors:浏览器尝试发出 CORS(跨域资源共享)请求。您可以在此处阅读有关 CORS的更多信息。
  • navigate:浏览器正在从一个页面导航到另一个页面,例如单击链接、接收重定向或打开书签时。

Sec-Fetch-Dest

告诉网络服务器请求的Sec-Fetch-Dest目的地,即在什么地方等待资源。在上面的例子中,它是一个<img>加载资源的元素,所以目的地是image.

一些可能的值是:

  • empty: 当资源通过fetch()或 XHR 加载时。
  • document:当资源在顶级导航中加载时。
  • image: 当资源被加载到<img>标签中时。
  • worker: 当资源通过new Worker(...).
  • iframe: 当资源加载到<iframe>.

该值可以是任何能够加载外部资源的元素,因此audioaudioworkletembedfontframemanifestobjectpaintworkletreportscriptserviceworkersharedworkerstyletrackvideo,xslt等也是可能的。

Sec-Fetch-User

Sec-Fetch-User头告诉网络服务器由于用户交互(例如通过单击链接)而发起的导航请求(理论上)。该值始终为?1。当由于浏览器不考虑“用户激活”而导致导航发生时,根本不会发送此标头。

浏览器支持

在撰写本文时,Chrome、Edge 和 Opera 支持获取元数据标头。但不用担心,您可以以完全向后兼容的方式实施策略。您可以在此处查看最新状态。

实施隔离政策

获取元数据标头如此出色的原因是它们允许我们这样做。

 

根据客户端上下文阻止服务器端的请求是我们以前从未有过的能力。但现在我们这样做了,所以让我们使用它并实施隔离策略

要创建隔离策略,您需要一个过滤器、中间件等,使您能够根据 HTTP 请求标头阻止请求。

我将使用 NodeJS 作为示例,但是在任何开发框架中实现这样的中间件通常都是微不足道的。

我们将从这个骨架开始:

app.use(function (req, res, next) {})

1. 向后兼容

通过允许根本没有获取元数据标头的请求开始您的策略。否则,使用尚不支持获取元数据的浏览器的人将无法访问您的网站。

if (!req.headers['sec-fetch-site']) {
  return next()
}
if (!req.headers['sec-fetch-mode']) {
  return next()
}
if (!req.headers['sec-fetch-dest']) {
  return next()
}

2.允许所有来自同一来源的请求

要使您的应用程序正常工作,请允许来自同一来源的所有交互。

if (req.headers['sec-fetch-site'] === 'same-origin') {
  return next()
}

3. 允许不是来自其他网站的请求

有时请求来自用户打开书签或在 URL 栏中输入。在这些情况下, 的值Sec-Fetch-Sitenone,所以让我们允许它。

if (req.headers['sec-fetch-site'] === 'none') {
  return next()
}

4. 允许导航

要使其他网站能够链接到您的页面,必须允许导航。但是,仅允许导航也将允许跨站点 POST 请求、框架和其他我们不一定想要的东西。

为安全起见,请验证 HTTP 方法是否为GET并且资源目标是否为document.

if (
  req.method === 'GET' &&
  req.headers['sec-fetch-mode'] === 'navigate' &&
  req.headers['sec-fetch-dest'] === 'document'
) {
  return next()
}

5.阻止任何其他请求

如果一个请求到目前为止不符合任何规则,则拒绝它。

return res.status(403).json({
  error: 'Request blocked by the isolation policy.',
})

6. 必要时放​​宽政策

您可能希望允许一些跨域或跨站点交互。

允许子域

要允许来自您自己的子域的请求,请允许具有Sec-Fetch-Site值的请求通过same-site

if (req.headers['sec-fetch-site'] === 'same-site') {
  return next()
}

允许取景

要使其他网站能够在 iframe 中加载您的页面,GET请允许在目标为iframe.

if (
  req.method === 'GET' &&
  req.headers['sec-fetch-mode'] === 'navigate' &&
  req.headers['sec-fetch-dest'] === 'iframe'
) {
  return next()
}

7. 测试策略

我们现在实施了相当严格的隔离政策。这是全部内容(在我的示例中,我不允许 iframe 或子域)。你可以在这里fork 和玩代码。

app.use(function (req, res, next) {
  // If fetch metadata is not supported, allow the request.
  if (!req.headers['sec-fetch-site']) {
    return next()
  }
  if (!req.headers['sec-fetch-mode']) {
    return next()
  }
  if (!req.headers['sec-fetch-dest']) {
    return next()
  }

  // If the request originates from your own web application, allow it.
  if (req.headers['sec-fetch-site'] === 'same-origin') {
    return next()
  }

  // If the request doesn't originate from a website at all (bookmark, etc.) then allow it.
  if (req.headers['sec-fetch-site'] === 'none') {
    return next()
  }

  // If the request is a navigation GET request, allow it.
  if (
    req.method === 'GET' &&
    req.headers['sec-fetch-mode'] === 'navigate' &&
    req.headers['sec-fetch-dest'] === 'document'
  ) {
    return next()
  }

  // If no rules matched, block the request.
  return res.status(403).json({
    error: 'Request blocked by the isolation policy.',
  })
})

我在这里创建了一个小测试页。它尝试加载图像、发布 HTML 表单并构建受保护的网站。如果你打开网站,你会发现这三个都失败了。

 还有一个链接。单击链接,页面正确加载。

 

此外,在 Firefox 或 Safari 中加载页面工作正常,因此我们的向后兼容性似乎得到了检查。

8. 部署策略

首次将策略部署到生产环境时,建议使用仅记录日志的方法。

该策略将保持不变,但不是阻止请求,而是记录一个请求会因为 X 而被阻止,然后让该请求通过。

这样,您将很快知道策略是否会破坏某些内容,以及在最终部署强制执行策略之前是否需要添加一些内容。

结论

获取元数据标头是一项出色的浏览器功能,它使开发人员能够以前所未有的方式保护 Web 应用程序。

实施有效的策略很简单,并且可以轻松地放宽以适应任何特殊需求,例如允许框架或来自子域的交互。

将策略部署到生产环境时,建议从仅记录方法开始。然后,当您对它不会破坏任何内容感到满意时,稍后部署执行策略。

根据浏览器的不同,浏览器支持仍然缺乏或处于试验阶段,但标头已经可以向后兼容的方式使用。

参考

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值