针对多个浏览器选项卡,防止PHP中的CSRF攻击

时间:2017-08-30 18:58:52

标签: php security csrf

我知道,针对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
}

1 个答案:

答案 0 :(得分:0)

如果您想为每个表单实例添加一个新令牌,我不会发现您的实现存在问题。两个选项卡都是由用户合法打开的,因此有意义的是两个令牌都会被接受。

至于CSRF保护,我认为它完好无损。第三个站点在没有令牌的情况下无法模拟用户,并且具有两个或三个有效令牌的用户不会使攻击者的任务更容易。

如果您对前端进行编码,则可以向document onUnload event注册事件处理程序,并让处理程序发出POST请求,告知服务器正在卸载哪个令牌。这样后端可以从会话中删除令牌。这可能比它的价值更麻烦,因为如果用户点击浏览器BACK按钮,她将重新加载无效的令牌,并且在提交表单时会因错误而感到沮丧。