我在应用程序中构建了 CSRF 保护,只需在每个页面加载时生成随机令牌,将其置于会话中,然后将令牌绑定到{{ 1}}标签属性,如:
<body>
然后在每个表单操作或ajax请求中,我只需从body标签中获取令牌并将其发送出去。
这很有效,除了一个大问题。用户正在打开应用程序的多个选项卡,我看到令牌冲突。例如,用户加载第一页并生成令牌,然后他们切换选项卡,加载另一页,生成新令牌。最后,他们切换回第一页并提交格式化操作。这会导致无效的CSRF令牌错误。
重新设计此选项以防止与多个标签发生冲突的最佳方法是什么,同时尽可能保证其安全。
在登录正确的解决方案时,只是生成单一令牌,而不是在每次加载页面时生成新令牌?
答案 0 :(得分:15)
假设您的应用程序使用SSL加以保护,那么通过在每个页面加载时生成新标记实际上没有创建任何值。它不会阻止攻击者利用XSS漏洞 - 他们无论如何都可以访问新生成的令牌。
记住CSRF令牌防范的内容:恶意的第三方页面盲目地尝试将数据发布到您的应用程序,希望用户登录。在这种攻击中,攻击者永远无法访问CSRF令牌,所以频繁更换它并不好。
不要浪费时间和资源来跟踪每个会话的多个令牌。只需在开始时生成一个即可完成。
答案 1 :(得分:2)
您可以在登录时使用单个令牌。正如@ Josh3736指出的那样,这很好用。
如果您确实希望每页有一个令牌,则可以在$ _SESSION中存储一组有效令牌。然后,您将在使用它们时使单个令牌到期。您也可以选择在超时期限后使它们过期,但只有在超时短于会话超时时才有意义。但是,再次,你真正完成了什么?对于CSRF而言,单个令牌完全正常。
答案 2 :(得分:0)
我遇到了这个确切的问题,在页面加载时,我正在生成这样的CSRF令牌:
$_SESSION["token"] = bin2hex(random_bytes(32));
多个标签导致CSRF不匹配,所以我改为:
if (!isset($_SESSION['token'])) {
$_SESSION['token'] = bin2hex(random_bytes(32));
}
服务器端,我这样做(精简版):
$csrf = preg_replace("/[^a-zA-Z0-9]+/", "", $_POST["token"]);
if ($csrf !== $_SESSION["token"]) {
// Give an error
die ("No valid CSRF token provided");
}
这可以防止XSS攻击,但是并不能阻止某人进入页面,获取PHP会话ID(来自标头)和CSRF令牌,并使用诸如Postman或WGET之类的工具来汇总hack API帖子,等
这可能就是为什么存在这个问题的原因...了解CSRF令牌用于防御的范围。