我最近研究了CSRF。推荐的缓解策略是实施Synchronizer Token Pattern。
当您查看详细信息时,就会出现这些令牌需要多久更改一次的问题。同样,一般建议是每个会话的令牌就足够了。如果您需要额外的安全性(或者如果您遇到泄漏 CSRF 令牌是一个问题的情况),您可以使用每个请求的令牌。
在验证大型网站如何实现 CSRF 保护时,我查看了 github.com。令我感到困惑的是,我无法理解他们为什么要像他们那样实施他们的方法。
我突然想到他们使用每个请求令牌(甚至更多,每个表单令牌都是唯一的)。但是,旧令牌在使用后不会失效。我举个例子。
让我们考虑一下 github 个人资料页面:https ://github.com/settings/profile 。
对 GET 请求的响应包含三个表单,其中的authentity_token作为隐藏的输入参数。如果我没记错的话,github 是基于 Rails 构建的。所以这很合适,因为名称与Rails CSRF 保护机制的默认名称匹配。此外,令牌的长度为 64 字节,与Rails 屏蔽 CSRF 令牌方法的长度相匹配。
代币附在以下表格上:
- 令牌 1:退出按钮
- 令牌 2:更新配置文件按钮
- 令牌 3:保存工作资料按钮
如果重新加载页面,所有三个标记的值都会更改。
让我们考虑一个更新我的个人资料的后续 POST。
- 如果我提交了预期的 Token 2,则执行该操作。
- 如果我提交了无效令牌,则不会执行该操作(服务器返回 HTTP 422)。
到目前为止一切顺利,现在到了我不明白的事情。当我执行以下请求之一时,也会执行该操作,而不会出现错误:
- 从页面提交另一个令牌(例如上面的令牌 1 或令牌 3)。
- 再次提交令牌 2。因此,旧令牌不会因被使用而失效。这可以多次工作。
- 重新加载页面,但提交旧令牌之一。
所以我想它归结为:为什么 github 实现每个请求(甚至每个表单)令牌,但从不使它们无效/验证它们是否属于正确的表单元素?
或者也许我完全错了,github实际上实现了一种不同的方法(比如加密令牌模式)。