PHP CSRF令牌代码.足够的安全


PHP CSRF Token code. Sufficient security?

最近我尽我所能保护我的网站不受XSS的攻击,现在我正在保护网站不受CSRF的攻击,在观看和阅读了一些关于这个问题的文章后,我创建了下面的代码。

我想知道我的实现是否正确在数据库中使用字符串来帮助安全。有什么是我应该做的吗??我应该检查两边的数据库吗?

代码:

if(!isset($_SESSION['register_token'])){
  $keytype = 'register';
  $getregisterkey = mysql_query("SELECT key FROM tokenkeys WHERE type='".$keytype."' ") or die(mysql_error()); 
  while ($row = mysql_fetch_array($getregisterkey))
  {
$registerkey = mysql_real_escape_string($row['key']);
  }; 

  $_SESSION['register_token']=sha1(uniqid(rand(), TRUE).$registerkey);
  $_SESSION['register_token_time']=time();
}
<input type="hidden" name="token" value="<?php echo $_SESSION['register_token'];?>" />

if($_POST['register_token']==$_SESSION['register_token']){
  $register_token_age=time()-$_SESSION['register_token_time'];
  if($register_token_age=>300){
    //process form
  } else{
    //valid token but expired
  }
} else{
  die('Access Forbidden')
}

XSRF令牌仅与发送页面的通道一样安全。在此表单上使用https且仅使用https,并且仅将其提交到https端点。否则,MITM可以在提供或提交表单时从表单获取XSRF令牌。

while ($row = mysql_fetch_array($getregisterkey))
{
    $registerkey = mysql_real_escape_string($row['key']);
}; 

将永远不会在没有行时执行,这种情况下,当您稍后执行

时,您不会从$getregisterkey中获得太多熵。
$_SESSION['register_token']=sha1(uniqid(rand(), TRUE).$registerkey);

所以我会确保你的实现失败-快速如果有零行返回。也许更改为if ($row = mysql_fetch_array(...)) { ... } else { /* abort */ },因为您没有从额外的行中获益。

rand()要么是真正随机的,要么是密码学上强的PRNG。

我不熟悉PHP的标准库,但是[wikipedia]建议rand()不是加密强的。维基百科说

有一些建议为PHP增加强随机数生成。

PHP的强加密建议使用openssl_random_pseudo_bytes()

不要使用rand()或mt_rand()

要在PHP中生成加密强随机数,必须使用OpenSSL库的openssl_random_pseudo_bytes()函数。

如果您使用弱随机性,那么攻击者可以观察您生成的数字(通过请求表单的多个版本并解析出隐藏的输入),并使用它来找出下一个数字可能是什么并伪造CSRF令牌。

如果攻击者修改了'register_token_time'会话属性,那么他们可以避免XSRF检查。

例如,如果你有一个页面

$_SESSION[$_POST['x']] = $_POST['y'];
那么攻击者可以POST
x=register_token&y=pwnd

替换存储在会话中的register_token,然后用

发送一个post
token=pwnd
并绕过XSRF保护