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

发布于 2024-12-11 21:23:05 字数 950 浏览 0 评论 0原文

最近,我尽了最大努力保护我的网站免受 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')

}

Having recently protected my site the best I can against XSS I am now in the process of protecting against CSRF, having watched and read some articles on the matter I have created the below code.

I would like to know whether my implementation is correct in using a string in the database to help security. Is the anything I should be doing differently?? Should I be checking the database on both sides???

Code:

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')

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

梦回梦里 2024-12-18 21:23:05

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,然后发送带有 XSRF 保护的帖子

token=pwnd

并绕过您的 XSRF 保护。

XSRF tokens are only as safe as the channel over which the page is sent. Use https and only https on this form and only submit it to an https endpoint. Otherwise a MITM can get your XSRF token from the form as it is served, or as it is submitted.

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

will never execute when there are zero rows which case you don't get much entropy from $getregisterkey when you later do

$_SESSION['register_token']=sha1(uniqid(rand(), TRUE).$registerkey);

so I would make sure that your implementation fails-fast if there are zero rows returned. Maybe change to if ($row = mysql_fetch_array(...)) { ... } else { /* abort */ } since you get no benefit from extra rows.

The rand() needs to be either truly random or a cryptographically strong PRNG.

I am not familiar with PHP's standard libraries but [wikipedia] suggests rand() is not cryptographically strong. wikipedia says

There are proposals for adding strong random number generation to PHP.

Strong cryptography in PHP suggests using openssl_random_pseudo_bytes()

Don’t use rand() or mt_rand()

To generate a cryptographically strong random number in PHP you have to use the function openssl_random_pseudo_bytes() of the OpenSSL library.

If you use weak randomness then an attacker can observe the numbers you generate (by requesting multiple version of the form and parsing out the hidden input) and use that to figure out what the next numbers might be and forge CSRF tokens.

If an attacker modify the 'register_token_time' session property then they can avoid your XSRF checks.

For example, if you have a page that does

$_SESSION[$_POST['x']] = $_POST['y'];

then an attacker can POST

x=register_token&y=pwnd

to replace the register_token stored in the session and then send a post with

token=pwnd

and bypass your XSRF protection.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文