CSRF 保护有多种方法。
传统方式(“同步器令牌”模式 https://en.wikipedia.org/wiki/Cross-site_request_forgery#Synchronizer_token_pattern)通常涉及为每个请求设置唯一的有效 Token 值,然后在随后发送请求时验证该唯一值。这通常是通过设置隐藏表单字段来完成的。令牌值通常是短暂的并与该会话相关联,因此如果黑客尝试重用他们之前在页面上看到的值,或者尝试猜测该值,他们可能会失败。因此,只有来自您的应用程序的请求才有效,来自您的应用程序/域外部的伪造请求(也称为跨站点请求伪造)将会失败。
其缺点是它要求您的应用程序在所有 HTML 表单上设置此隐藏令牌。这些页面现在必须由应用程序动态生成,而以前它们可能是静态 HTML。它还可能会破坏后退按钮(因为您需要刷新表单以重新生成另一个唯一的 CSRF 值)。您现在还需要跟踪服务器端的有效令牌并检查任何使用有效令牌的请求。这可能需要相当多的额外努力来实施和维护。
另一种方法(称为“Cookie 到标头标记”模式 https://en.wikipedia.org/wiki/Cross-site_request_forgery#Cookie-to-header_token)是为每个会话设置一次 Cookie,然后让 JavaScript 读取该 cookie 并设置自定义 HTTP 标头(通常称为X-CSRF-TOKEN
or X-XSRF-TOKEN
要不就XSRF-TOKEN
)具有该值。任何请求都会发送标头(由 Javascript 设置)和 cookie(由浏览器设置为标准 HTTP 标头),然后服务器可以检查X-CSRF-TOKEN
header 与 cookie 标头中的值匹配。这个想法是,只有在同一域上运行的 JavaScript 才能访问 cookie,因此来自另一个域的 JavaScript 无法将此标头设置为正确的值(假设该页面不易受到允许访问此 cookie 的 XSS 攻击) 。即使是虚假链接(例如在网络钓鱼电子邮件中)也不起作用,因为即使它们看起来来自正确的域,也只会设置 cookie,但不会设置X-CSRF-TOKEN
header.
这比 Synchronizer 令牌模式更容易实现,因为您不需要为每个表单的每次调用设置令牌,并且检查也相对简单(只需检查 cookie 与标头匹配)而不是跟踪 CSRF 令牌有效性。您所需要做的就是为每个会话将 cookie 设置为随机值。一些前端框架甚至会在看到 cookie 时自动为您生成标头(例如AngularJS 就是这样做的 https://docs.angularjs.org/api/ng/service/%24http#cross-site-request-forgery-xsrf-protection例如)。
缺点是它需要 JavaScript 才能工作(但如果你的应用程序基本上没有 JavaScript 就无法工作,那么这可能不是问题),而且它只适用于 JavaScript 发出的请求(例如 XHR 请求) - 常规 HTML 表单请求不会设置标题。对此的一个变体(“双重提交 Cookie”模式 https://en.wikipedia.org/wiki/Cross-site_request_forgery#Double_Submit_Cookie)把X-CSRF-TOKEN
隐藏表单字段中的值而不是 HTTP 标头中的值来解决此问题,但仍然使服务器端逻辑比传统的 Synchronizer 令牌模式更简单。但应该注意的是OWASP 指出了双重提交方法的一些弱点 https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.md#double-submit-cookie,当攻击者能够设置 cookie(这通常比读取 cookie 更容易)时,因此建议在这种情况下验证 CSRF 令牌。
此外,同步器令牌模式可以允许额外的控制来强制执行流程(例如,仅当应用程序认为您已发送有效请求以获取该表单时,才会设置隐藏字段 CSRF 令牌)。
哦,一些安全扫描会发现 cookie 未使用HTTP-Only
标志,以便 JavaScript 可以读取 - 但这是故意的,因为它需要能够读取!错误警报。你会认为只要你使用一个常见的名字,比如X-CSRF-TOKEN
他们知道不要标记这一点,但经常看到它被标记。