通过 AJAX 获取 CSRF 令牌

Posted

技术标签:

【中文标题】通过 AJAX 获取 CSRF 令牌【英文标题】:Getting CSRF token via AJAX 【发布时间】:2016-02-08 01:05:57 【问题描述】:

我计划构建一个前端和后端分离的应用程序(仅使用 ajax 请求)。我不允许跨站点 ajax 请求。我可以通过添加类似 /csrf 的 API 来生成带有 ajax 调用的 csrf 令牌,它返回如下内容:csrf: 'token' 以下网站说我绝对不能这样做:https://github.com/pillarjs/understanding-csrf

确保不能使用 AJAX 访问 CSRF 令牌!不要仅仅为了获取令牌而创建 /csrf 路由,尤其是不要在该路由上支持 CORS!

有什么具体原因吗?我了解 CORS 背后的原因 - 已禁用,但通过 ajax 提供 csrf 令牌是否存在任何固有的安全风险?

【问题讨论】:

github.com/pillarjs/understanding-csrf/issues/6 如果您不启用 CORS,那么同源策略将阻止检索 CSRF 令牌。听起来文档是错误的。 【参考方案1】:

这不是安全风险,原因如下:

您为什么还要通过 AJAX 公开 CSRF 令牌?

从可用性的角度来看,如果您不提供通过 AJAX 获取 CSRF 令牌的方法,并且不使用双重提交 Cookie 方法(完全解决此问题),那么您的网站可能会使用起来很糟糕,因为任何打开多个标签的人都会遇到有关 CSRF 令牌不匹配的错误。

在使用 CSRF 令牌的网站上,如果您在表单上挂钩以在提交 POST 请求之前获取最新的 CSRF 令牌,那么用户体验会好得多。

现代单页应用程序网站(例如使用 React 或 Angular 构建的通用网站)也需要一种轻松获取 CSRF 令牌的方法,这些网站受益于 CSRF 令牌提供的投影,就像传统的仅基于服务器渲染的网站一样。

CSRF 令牌与会话绑定

CSRF 令牌绑定到会话 — 即使您没有明确登录,它仍然需要绑定到客户端和服务器之间跟踪的会话。 如果您的会话令牌有一个仅限 HTTP 的 cookie(这是最佳实践,可以防止 XSS 攻击窃取它并帮助保护您免受 CSRF 攻击)没有人可以通过远程域从请求中读取您的 CSRF 令牌(即使浏览器不强制执行 CORS!)。

跨域资源共享

CORS 位在这样的讨论中出现了很多,但它实际上是一个红鲱鱼。

安全性实际上归结为为您的 sesison cookie 使用仅 HTTP cookie,并为带有会话令牌的 cookie 使用相同的域策略 — 所有浏览器从一开始就支持这一点(带有 MSIE 的边缘案例警告将 cookie 公开给其他浏览器不提供的子域)。

如果远程站点上的某些 javascript 无法读取您的会话令牌 — 如果它位于您域上的仅 HTTP cookie 中,它就无法读取 — 那么它就无法读取您的 CSRF 令牌!

如果使用仅限 HTTP 的 cookie 和 CSRF 令牌,我能否抵御 CSRF 和 XSS 攻击?

您应该免受 CSRF 攻击,并且您对某些类型的 XSS 攻击的保护有限,但 XSS 仍然存在风险。

如果有人找到了使用 XSS 在您的网站上执行任意 JavaScript 并创建来自您的域的请求的方法,那么 CSRF、仅 HTTP cookie 或会话指纹都不会保护您 — 任何执行的操作都可以看起来就像是由用户触发的一样。

针对请求的 CAPTCHA 可以提供一些额外的保护,但对于不可逆转的破坏性或可能代价高昂的操作,外部确认操作(例如通过电子邮件、SMS 等)是一个好主意。

但是,安全不是比后悔更好吗?

如果您仍然担心 CSRF 的 AJAX 端点可能会以某种您不理解的方式成为矢量,请考虑能够执行来自您的域的请求并解析响应的远程脚本同样可以触发对带有表单的页面的请求,并从 <form> 上的 <input> 值获取 CSRF 令牌。

同样,如果有人能够在您的网站上执行任意 JavaScript 并以他们所针对的当前用户的身份发出和读取请求,那么从您的 DOM 中读取 CSRF 令牌并从表单已经在页面上,而不是乱用 AJAX。

总结

使用仅 HTTP cookie 和 CSRF 令牌来帮助防止 CSRF 和 XSS 攻击。

当使用 HTTP Only cookie 提供通过 AJAX 获取 CSRF 令牌的方法时是不可利用的。

提供一种通过 AJAX 获取 CSRF 令牌的方法是一件好事,它可以在不影响安全性的情况下带来更好的用户体验。

考虑添加验证码和/或外部验证以确认适当的操作,以防止 XSS。

双重提交 Cookie 方法实际上消除了遇到陈旧 CSRF 令牌的机会,并且避免了任何需要担心通过 Ajax 获取令牌的问题,但是 be aware of the pitfalls(例如,向 cookie 添加签名,检查签名对提交时的哈希是否有效,使用cookie 前缀)。

或者,您不能在给定会话中轮换用户 CSRF 令牌(某些中间件实际上这样做是为了完全避免处理该问题),但值得注意的是,这不是大多数 CSRF 令牌库的典型默认行为。

来自开放 Web 应用程序安全项目的 OWASP CSRF Cheat Sheet 是一个很好的资源,而且更详细。

【讨论】:

以上是关于通过 AJAX 获取 CSRF 令牌的主要内容,如果未能解决你的问题,请参考以下文章

在测试中获取 CSRF 令牌,“CSRF 令牌无效” - 功能性 ajax 测试

在没有表格的情况下获取 Codeigniter CSRF 令牌?

在测试中获取 CSRF 令牌

为啥恶意站点在攻击前无法通过 GET 获取 CSRF 令牌?

如何生成和验证 csrf 令牌

Laravel 4 CSRF 表单通过 Ajax 调用提交