3

如果我想保护我的站点和用户免受跨站点伪造 (CSRF) 攻击,我可以$token = md5( time() * rand );在每个具有表单的页面上生成一个唯一令牌。令牌在隐藏的输入字段中提交,echo '<input type="hidden" name="token" value="'.$token.'">';同时存储在会话变量$_SESSION['token'] = $token;中。

我将检查是否有任何提交的表格if($_POST['token'] == $_SESSION['token'])并相应地进行。

但是,有些用户可能会同时执行多项任务。这是我现在实际上正在做的事情,而我正在发布这个。

在撰写我的帖子时,我打开不同的窗口/选项卡以可能研究信息或查看有关堆栈溢出的其他一些问题。堆栈溢出让我可以毫无问题地提交表单。

但是,如果我要在我的网站上这样做——这意味着在撰写帖子/表单的同时浏览其他页面——$token每次我从我的网站上拉出不同的页面时,我都会重新生成。input在我正在处理的表单上制作隐藏令牌并最终想要提交不正确的,因为它不再匹配$_SESSION['token']变量,当我访问不同的页面时它已经重新生成......

有什么好的想法可以防止这个问题,或者有什么更好的解决方案来阻止 CSRF?

我想让我的用户执行多项任务并希望受到 CSRF 的保护......

4

2 回答 2

4

由于单个 CSRF,我对你所说的内容有同样的问题,除非他们提交最新的页面,否则它会被替换,但如果你使用带有会话的数组,它应该可以解决你的问题。此外,您可能想要包含验证码,我推荐 Google 的 Recaptcha。

session_start();
function createToken(){
    $token = sha1(uniqid(mt_rand(), true));
    $_SESSION['Tokens']['Token'][] = $token;
    $_SESSION['Tokens']['Time'][] = time() + (10 * 60); #10 min limit
    #you can omit/change this if you want to not limit or extend time limit
    return $token;
}

function checkToken($token){
    clearTokens();
    foreach($_SESSION['Tokens']['Token'] as $key => $value){
        if($value === $token){
            return true;
        }
    }
    return false;
}

function clearTokens(){
    foreach($_SESSION['Tokens']['Time'] as $key => $value){
        if($value <= time()){
            unset($_SESSION['Tokens']['Token'][$key], $_SESSION['Tokens']['Time'][$key]);
            #remove last parameter if you aren't using token time limit
        }
    }
}

你的 HTML:

<input type="hidden" name="token" value="<?php createToken(); ?>">

PHP 令牌检查器

if(isset($_POST['token']) && checkToken($_POST['token'])){
    #valid token
}else{
    #create error message saying that they tried to repost data or session token expired
}
于 2013-08-14T18:04:47.423 回答
2

即使在选项卡和窗口之间,浏览器也应该保持正确的会话 ID,会话 ID 应该相同。(危险的假设应该跨浏览器进行测试以确保)

根据会话 ID 生成更多应该有效的令牌。

所以你可以检查这样的事情。

$tokenCorrect = false;

foreach($_SESSION['tokens'] as $token) {
  if ($token !== $_POST['token'])
     continue;
  $tokenCorrect = true;
}

if ($tokenCorrect == false) {
   die(); // 
   // Maybe log to database ?? but watch if possible Denial of Service because somebody can write your disk/ shared diskspace full with only making fast requests with a invalid CSRF token
}
于 2013-08-14T18:02:16.927 回答