19

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

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

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

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

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

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

4

3 回答 3

20

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

请记住 CSRF 令牌所防御的内容:恶意第三方页面盲目地尝试向您的应用程序发布数据以希望用户登录。在这种攻击中,攻击者永远无法访问 CSRF 令牌,因此更改它经常没有好处。

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

于 2013-11-18T20:35:43.067 回答
2

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

如果您真的希望每页有一个令牌,您可以在 $_SESSION 中存储一组有效令牌。然后,您将在使用单个令牌时使它们过期。您也可以选择在某个超时期限后使它们过期,但这仅在超时短于会话超时时才有意义。但是,再一次,你真的用这个来完成什么?单个令牌对于 CSRF 目的来说是完全可以的。

于 2013-11-18T20:31:57.930 回答
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");
}

This may protect against XSS attacks, but it wouldn't stop someone going to the page, getting the PHP session ID (from headers) and the CSRF token and using a tool like Postman or WGET to put together hack API posts, etc.

That may be why this question exists... understanding the scope of what the CSRF token is for protecting against.

于 2020-06-23T01:29:52.660 回答