PHP CSRF令牌代码。足够的安全性?

时间:2011-10-23 18:32:03

标签: php sql security csrf

最近保护我的网站我能够最好地对抗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')

}

1 个答案:

答案 0 :(得分:1)

XSRF令牌仅与发送页面的频道一样安全。仅在此表单上使用httpshttps,并仅将其提交到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()必须是真正随机的或cryptographically strong PRNG。

我不熟悉PHP的标准库,但[维基百科]认为rand()不具有加密功能。 wikipedia

  

有建议在PHP中添加强大的随机数生成。

Strong cryptography in PHP建议使用openssl_random_pseudo_bytes()

  

请勿使用rand()或mt_rand()

     

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

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

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

例如,如果您有一个页面

$_SESSION[$_POST['x']] = $_POST['y'];
然后攻击者可以POST

x=register_token&y=pwnd

替换会话中存储的register_token,然后发送带

的帖子
token=pwnd

并绕过您的XSRF保护。