【发布时间】:2017-08-30 18:58:52
【问题描述】:
我知道,针对 CSRF 攻击有两种主要的解决方案。
- 每个会话一个令牌
- 所有独特形式的令牌
我选择了第二个,但是有问题。如果访问者在两个选项卡中打开表单,则第二个选项卡上的表单标记会覆盖之前的标记。我写了一个代码来解决这个问题。我的问题是:这是一个好的解决方案,还是我必须努力解决? (示例中只有最重要的部分。)
##### CONFIG & FUNCTION.PHP #####
// INITIALIZING - RUN ONLY ONCE
$_SESSION["csrf_tokens"]["postcomment"] = array ();
$_SESSION["csrf_tokens"]["postcommentedit"] = array ();
// etc, etc.
function makearandomtoken ()
{
// a simple but secure way
return bin2hex (openssl_random_pseudo_bytes (32));
}
##### POSTCOMMENT_FORM.PHP #####
// new form for comment under a post, so create a new token
$created_token = makearandomtoken ();
array_push ( $_SESSION["csrf_tokens"]["postcomment"], $created_token );
// the form (only with important parts)
print "<form>\n";
print "<input type=\"hidden\" name=\"token\" value=\"$created_token\">\n";
print "</form>\n";
##### POSTCOMMENT_EXECUTE.PHP #####
// get the token from POST variable
$received_token = filter_input (INPUT_POST, 'token', FILTER_UNSAFE_RAW);
// check it
if ( in_array($received_token, $_SESSION["csrf_tokens"]["postcomment"]) )
{
// VALID token, disable it
$token_index = array_search($received_token, $_SESSION["csrf_tokens"]["postcomment"]);
unset ($_SESSION["csrf_tokens"]["postcomment"][$token_index]);
}
else
{
// INVALID token -> CSRF attempt
die (); // or do anything
}
【问题讨论】:
-
如果您的应用需要打开 2 个选项卡,则每次登录时执行一个 CSRF 令牌,如果不是很好,则用户不应打开多个选项卡。也仅在您以这种方式呈现表单时设置标记,它们可以为非基于表单的页面提供多个选项卡。
-
看起来不错。唯一缺少的是清除旧未使用令牌的策略和/或限制每个用户的有效令牌数量。
-
您必须限制令牌的数量,否则攻击者可以使用它来填充会话。并且由于会话大小仅受 PHP 脚本内存限制的限制,它可以使用多个帐户对服务器进行 DoS。