带有自定义标头的 CSRF 保护(并且没有验证令牌)

信息安全 csrf
2021-09-05 06:15:06

对于 REST-api,检查自定义标头的存在似乎足以防止 CSRF 攻击,例如客户端发送

“X-请求者:随便”

并且服务器检查“X-Requested-By”是否存在,如果找不到标头则丢弃请求。标头的值无关紧要。这就是 Jersey 1.9 的 CsrfProtectionFilter 的工作原理,并在此博客文章中进行了描述:http: //blog.alutam.com/2011/09/14/jersey-and-cross-site-request-forgery-csrf/该博客文章还链接到 NSA 和斯坦福大学的论文,指出自定义标头本身就足够保护:

第一种方法涉及为每个 REST 请求设置自定义标头,例如 X-XSRF-Header。这个头的值无关紧要;简单地说,存在应该可以防止 CSRF 攻击。如果请求进入没有自定义标头的 REST 端点,则应丢弃该请求。

通过表单、图像、iframe 等执行的来自 Web 浏览器的 HTTP 请求无法设置自定义 HTTP 标头。从具有自定义 HTTP 标头的浏览器创建 HTTP 请求的唯一方法是使用诸如 Javascript XMLHttpRequest 或 Flash 之类的技术。这些技术可以设置自定义 HTTP 标头,但内置安全策略以防止网站相互发送请求,除非策略特别允许。这意味着网站 www.bad.com 无法向http://bank.example.com发送请求使用自定义标头 X-XSRFHeader,除非他们使用 XMLHttpRequest 之类的技术。除非 bank.example.com 域特别允许,否则该技术将阻止发出此类请求。这会产生一个只能通过 XMLHttpRequest(或类似技术)调用的 REST 端点。

请务必注意,此方法还可以防止从 Web 浏览器直接访问该 REST 端点。使用这种方法的 Web 应用程序需要通过 XMLHttpRequest 或类似技术与其 REST 端点进行交互。

来源:实施 REST 的指南

然而,似乎大多数其他方法都建议您生成一个令牌并在服务器上对其进行验证。这是过度工程吗?什么时候“存在”方法是安全的,什么时候还需要令牌验证?

4个回答

安全是关于纵深防御。目前只需检查该值就足够,但未来的技术和攻击可能会被利用来破坏您的保护。测试令牌的存在可以实现应对当前攻击所需的绝对最低限度的防御。添加随机令牌可以提高针对未来潜在攻击向量的安全性。使用按请求令牌还有助于限制 XSS 漏洞造成的损害,因为攻击者需要一种方法来为他们发出的每个请求窃取一个新令牌。

这与现代密码算法中使用的推理相同,其中n回合被认为是安全的最低要求,但2n+1在官方实现中选择回合(例如)以确保适当的安全边际。

进一步阅读:

TL;DR - 检查诸如“X-Requested-By”之类的非标准标头的存在应该足以防止 CSRF 攻击,而无需检查标头的值。

CSRF 攻击中不能设置非标准标头

Play 框架网站很好地分解了它:

简单地说,攻击者可以强制受害者浏览器发出以下类型的请求:

  • 所有 GET 请求
  • POST 请求的主体类型为 application/x-www-form-urlencoded、multipart/form-data 和 text/plain

攻击者不能:

  • 强制浏览器使用其他请求方法,例如 PUT 和 DELETE
  • 强制浏览器发布其他内容类型,例如 application/json
  • 强制浏览器发送新的 cookie,而不是服务器已经设置的那些
  • 强制浏览器设置任意标头,而不是浏览器添加到请求的普通标头

如果您考虑 CSRF 的攻击向量,这是有道理的:

  • GET 请求(例如 <img>、<iframe>) - 无法设置标头。
  • <form> 由用户点击提交 - 仅限于几个特定的​​标题。
  • <form> 由 JavaScript (HTMLFormElement.submit()) 提交 - 仅限于一些特定的标头。

JavaScript 受同源策略的约束,因此只有在满足以下条件之一的情况下才能添加非标准标头:

  • 它是“域内”(即从与请求目标相同的域加载)。
  • 允许通过CORS这样做。

XSS 攻击超出了这个问题的范围

可以在 XSS 攻击中设置非标准标头。无论标头的值如何,使用非标准标头来防止 CSRF 攻击不会使网站更容易(或更少)受到 XSS 攻击。非标准标头和 CSRF 令牌都容易受到 XSS 攻击。如果 XSS 攻击者可以在请求中设置非标准标头(例如域内 XHR),他/她当然可以访问设置在 cookie 中或嵌入 DOM 或 JavaScript 变量中的 CSRF 令牌。

参考

这里有一个类似的 SO question ,令人困惑,但得出了相同的结论。

此类非标准标头的一些示例:

  • 泽西岛/其他人认可的“X-Requested-By”(由 OP 提及)
  • jQuery 设置的“X-Requested-With”
  • Angular设置的“X-XSRF-TOKEN”
  • Play框架认可的“X-CSRF-TOKEN”

编辑:这个 csrf-request-builder 正在利用 Flash 中的漏洞,该漏洞现已修复。可以使用 JavaScript 发送复杂的请求,但是如果您指定其他标头元素,则会在实际请求之前发送预检 OPTIONS HTTP 请求。

我已经验证 Jersey 容易受到 CSRF 的攻击,并且 Jersey 的开发人员已收到通知。使用 Flash 和可能的其他脚本技术可以利用此漏洞。Jersey 很容易受到攻击,因为“X-Requested-By”HTTP 标头不在flash 的标头黑名单中。

我使用带有以下参数的CSRF-Request-Builder来构建发布请求:

file://var/code/CSRF-Request-Builder/csrf_payload.html#url=http://google.com&X-Requested-By=1&body={'test':1}

除非您真正了解 CSRF 的利用,否则您永远不应该使用自己的 CSRF 预防方法。CSRF 预防备忘单是一个很好的资源。

https://stackoverflow.com/a/11423778/14731提出了一个非常重要的观点:同源策略(SOP)关注的是防止跨域响应的读取,而不是请求的写入

这意味着,虽然您将来可能能够编写自定义标头,但您极不可能读取跨域请求的响应。因此,最好的 CSRF 保护包括从服务器读取秘密值,将其写回,并让服务器验证该值。

您不一定需要服务器端状态来完成此操作(Double-Submit CookiesEncrypted Token Pattern是其中的两个示例),但您应该在服务器上验证一些秘密值。