PHP-CSRF-如何使其在所有选项卡中均可工作? [英] PHP - CSRF - How to make it works in all tabs?

查看:62
本文介绍了PHP-CSRF-如何使其在所有选项卡中均可工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已阅读了有关最近几天如何防止CSRF攻击的信息.我将在每个页面加载中更新令牌,将令牌保存在会话中,并在提交表单时进行检查.

I have read about how to prevent CSRF-attacks in the last days. I am going to update the token in every pageload, save the token in the session and make a check when submitting a form.

但是,如果用户拥有,可以说在我的网站上打开了3个标签,而我只是在会话中存储了最后一个令牌,该怎么办?这将用另一个令牌覆盖该令牌,并且某些后续操作将失败.

But what if the user has, lets say 3 tabs open with my website, and I just store the last token in the session? This will overwrite the token with another token, and some post-action is going to fail.

我是否需要在会话中存储所有令牌,还是有更好的解决方案来使它正常工作?

Do I need to store all tokens in the session, or is there a better solution to get this working?

推荐答案

是的,使用存储令牌方法,您必须保留所有生成的令牌,以防万一它们再次出现.单个存储令牌不仅对于多个浏览器选项卡/窗口失败,而且对于后退/前进导航也失败.通常,您希望通过使旧令牌过期(按使用期限和/或此后发布的令牌数量)来管理潜在的存储爆炸.

Yes, with the stored-token approach you'd have to keep all generated tokens just in case they came back in at any point. A single stored-token fails not just for multiple browser tabs/windows but also for back/forward navigation. You generally want to manage the potential storage explosion by expiring old tokens (by age and/or number of tokens issued since).

另一种完全避免令牌存储的方法是发出使用服务器端机密生成的签名令牌.然后,当您拿回令牌时,您可以检查签名,如果匹配,就知道您已经签名了.例如:

Another approach that avoids token storage altogether is to issue a signed token generated using a server-side secret. Then when you get the token back you can check the signature and if it matches you know you signed it. For example:

// Only the server knows this string. Make it up randomly and keep it in deployment-specific
// settings, in an include file safely outside the webroot
//
$secret= 'qw9pDr$wEyq%^ynrUi2cNi3';

...

// Issue a signed token
//
$token= dechex(mt_rand());
$hash= hash_hmac('sha1', $token, $secret);
$signed= $token.'-'.$hash;

<input type="hidden" name="formkey" value="<?php echo htmlspecialchars($signed); ?>">

...

// Check a token was signed by us, on the way back in
//
$isok= FALSE;
$parts= explode('-', $_POST['formkey']);
if (count($parts)===2) {
    list($token, $hash)= $parts;
    if ($hash===hash_hmac('sha1', $token, $secret))
        $isok= TRUE;
}

有了这个,如果您获得带有匹配签名的令牌,就知道您已经生成了它.本身并没有多大帮助,但是您可以将令牌中除了随机性之外的其他内容放入令牌中,例如用户ID:

With this, if you get a token with a matching signature you know you generated it. That's not much help in itself, but then you can put extra things in the token other than the randomness, for example user id:

$token= dechex($user->id).'.'.dechex(mt_rand())

...

    if ($hash===hash_hmac('sha1', $token, $secret)) {
        $userid= hexdec(explode('.', $token)[0]);
        if ($userid===$user->id)
            $isok= TRUE

现在,每个表单提交都必须由获取表单的同一用户授权,这几乎使CSRF失败.

Now each form submission has to be authorised by the same user who picked up the form, which pretty much defeats CSRF.

另一件事是,放入令牌是一个有效时间,这样一来,即刻的客户入侵或MitM攻击就不会泄漏对用户永远有效的令牌,并且值会不断变化密码会重置,因此更改密码会使现有令牌失效.

Another thing it's a good idea to put in a token is an expiry time, so that a momentary client compromise or MitM attack doesn't leak a token that'll work for that user forever, and a value that is changes on password resets, so that changing password invalidates existing tokens.

这篇关于PHP-CSRF-如何使其在所有选项卡中均可工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆