CSRF令牌与多个选项卡冲突


CSRF token collisions with multiple tabs

我在应用程序中构建了CSRF保护,只需在每次页面加载时生成一个随机令牌,将其放入会话,然后将令牌绑定到<body>标签属性,如:

<body data-csrf-token="csrf_GeJf53caJD6Q5WzwAzfy">

然后,对于每个表单操作或ajax请求,我只需从body标记中获取令牌并将其发送出去。

除了一个大问题外,这很有效。用户正在打开应用程序的多个选项卡,我看到了令牌冲突。例如,用户加载第一个页面并生成一个令牌,然后切换选项卡,加载另一个页面,从而生成一个新令牌。最后,他们切换回第一个页面并提交一个格式化操作。这会导致无效的CSRF令牌错误。

重新设计它以防止与多个选项卡发生冲突,同时尽可能保持安全的最佳方法是什么。

简单地在登录时生成单个令牌,而不是在每次页面加载时生成新令牌,这是正确的解决方案吗?

假设您的应用程序使用SSL进行安全保护,那么在每次页面加载时生成新的令牌就不会产生任何价值。它不会阻止利用XSS漏洞的攻击者–无论如何,他们都可以访问新生成的令牌。

记住CSRF令牌的防御:恶意的第三方页面盲目地试图将数据发布到你的应用程序中,希望用户登录。在这种攻击中,攻击者永远无法访问CSRF令牌,因此频繁更改它没有好处。

不要浪费时间和资源来跟踪每个会话的多个令牌。只需在开始时生成一个即可完成。

登录时可以使用单个令牌。正如@Josh3736所指出的,这很好用。

如果您真的希望每页有一个令牌,可以在$_SESSION中存储一组有效的令牌。然后,您将在使用单个代币时使其过期。您也可以选择在某个超时期后使其过期,但只有当超时时间短于会话超时时间时,这才有意义。但是,再说一遍,你用这个真正实现了什么?对于CSRF而言,单个令牌是完全合适的。

我遇到了这个确切的问题,在页面加载时,我生成了一个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等工具来组合破解API帖子等

这可能就是这个问题存在的原因。。。了解CSRF令牌的保护范围。