实现反 CSRF 表单令牌的正确方法是什么?
关于 OWASP 有一个很好的解释:OWASP CSRF Prevention Cheat Sheet
简而言之,他们说有两种方法:
向每个用户会话添加一个随机令牌。仅当此令牌存在且正确时才会应用更改,否则应拒绝请求。令牌仅与 POST 请求一起发送很重要,因为 GET 请求可能会将令牌泄漏到不同的位置(浏览器历史记录、日志文件等)。
也可以为每个请求生成一个令牌,但这会导致可用性问题。例如,后退按钮将不再正常工作。但是当然会增加安全性。
还要确保(以进一步提高安全性)令牌仅通过 TLS 发送,因此中间不会有任何人出现问题。
另一种选择是使用某种质询 - 响应(例如验证码或一次性令牌)。
OWASP-Page 还列出了一些不起作用的措施。这些是
- 使用(秘密)cookies
- 不使用 GET 请求
- 多步交易
- 网址重写
正确构建 CSRF 保护的最佳方法:
别。
大多数常见的框架已经内置了这种保护(我认为是 ASP.NET、Struts、Ruby),或者已经存在已经过审查的库。(例如OWASP 的 CSRFGuard)。
根据您的上下文,另一种选择是强制重新验证用户身份,但仅限于特定的敏感操作。
根据您的评论,如果您需要自己实施,您的解决方案还不错,但我会简化它。
您可以生成一个加密随机随机数(足够长),将其存储在会话内存中,然后每 X 分钟更改一次(您也将到期时间存储在会话中)。在每个表单中,您将 nonce 包含在隐藏的表单字段中,并在表单发布时比较该值。
如果表单标记匹配,您就很清楚了。如果没有,您也可以接受例如先前的令牌,以处理边缘情况。
虽然我必须说我已经看到太多失败的尝试自己实现这一点,但我真的不推荐这条路。你最好还是找到一个最小的包来为你做这件事。
除了@Andreas Arnold 答案之外,还有另一种选择。我实现了Encrypted Token Pattern
来自 OWasp 的。
除了防止 csrf 攻击之外,它还具有实现对象安全的额外优势。
只有那些有权修改对象的人才能这样做。其他人将没有正确/有效的密文或密钥。我通常加密sessionId
, timestamp
,userId
和/或recordId
.
timestamp
防止重放攻击。
sessionid
隔离对此会话的
userId
更改仅隔离对此用户的更改。如果包含recordId
,则以额外处理为代价,您将获得更多安全性。
我认为在要散列的令牌中包含时间戳并将此时间戳作为隐藏表单字段包含在内(可能是十六进制编码为基本混淆)并没有太大错误(尽管我可能错了;)然后您可以检查提交时过时。