php 每个请求是否有新的 CSRF 令牌?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10466241/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 22:14:46  来源:igfitidea点击:

New CSRF token per request or NOT?

phpcsrf

提问by John

So I am reading around and was really confused about having a CSRF token, whetever I should generate a new token per each request, or just per hour or something?

所以我正在阅读并且对拥有 CSRF 令牌感到非常困惑,无论我应该为每个请求生成一个新令牌,还是每小时生成一个新令牌?

$data['token'] = md5(uniqid(rand(), true));
$_SESSION['token'] = $data['token'];

But let's say it's better to generate a token each hour, then I would need two sessions: token, expiration,

但是让我们说最好每小时生成一个令牌,然后我需要两个会话:令牌,到期,

And how will I proceed it to the form? Just put echo $_SESSION['token'] on the hidden value form and then compare on submit?

我将如何处理表格?只需将 echo $_SESSION['token'] 放在隐藏值表单上,然后在提交时进行比较?

采纳答案by Laurence

If you do it per form request - then you basically remove the ability for CSRF attacks to occur & you can solve another common issue: multiple form submission

如果您按表单请求执行此操作 - 那么您基本上消除了发生 CSRF 攻击的能力,并且您可以解决另一个常见问题:多表单提交

In simple terms - your application will only accept form input if the user ASKED for the form prior to the submission.

简单来说 - 如果用户在提交之前询问表单,您的应用程序将只接受表单输入。

Normal scenario:User A goes to your website, and asks for Form A, is given Form A plus a unique code for Form A. When the user submits Form A, he/she must include the unique code which was only for Form A.

正常场景:用户A访问您的网站,要求提供Form A,得到Form A和Form A的唯一代码。当用户提交Form A时,他/她必须包含仅用于Form A的唯一代码。

CSRF Attack scenario:User A goes to your website, and asks for Form A. Meanwhile they visit another "bad" site, which attempts a CSRF attack on them, getting them to submit for a fake Form B.

CSRF 攻击场景:用户 A 访问您的网站,并要求提供 Form A。同时他们访问另一个“坏”站点,该站点尝试对他们进行 CSRF 攻击,让他们提交伪造的 Form B。

But your website knows that User A never asked for Form B - and therefore even though they have the unique code for Form A, Form B will be rejected, because they dont have a Form B unique Code, only a Form A code. Your user is safe, and you can sleep easy at night.

但是您的网站知道用户 A 从未要求过表格 B - 因此即使他们有表格 A 的唯一代码,表格 B 也会被拒绝,因为他们没有表格 B 唯一代码,只有表格 A 代码。您的用户是安全的,您可以在晚上轻松入睡。

But if you do it as a generic token, lasting for an hour (like you posted above) - then the attack above might work, in which case you've not achieved much with your CSRF protection. This is because the application does not know that form B was never asked for in the first place. It is a generic token. The WHOLE POINT of CSRF prevention is to make each form token unique to that form

但是,如果您将其作为通用令牌进行,持续一个小时(就像您在上面发布的那样) - 那么上面的攻击可能会奏效,在这种情况下,您的 CSRF 保护并没有取得太大成果。这是因为应用程序不知道从一开始就从未要求过表单 B。它是一个通用令牌。CSRF 预防的全部要点是使每个表单令牌对该表单来说是唯一的

Edit: because you asked for more information:1 - you dont have to do it per form request, you can do it per hour/session etc. The point is a secret value that is given to the user, and resubmiited on the return. This value is not known by another website, and thus cannot submit a false form.

编辑:因为您要求提供更多信息:1 - 您不必按表单请求执行此操作,您可以每小时/会话等执行此操作。重点是提供给用户的秘密值,并在返回时重新提交。其他网站不知道该值,因此不能提交虚假表单。

So you either generate the token per request, or per session:

因此,您可以为每个请求或每个会话生成令牌:

// Before rendering the page:
$data['my_token'] = md5(uniqid(rand(), true));
$_SESSION['my_token'] = $data['my_token'];

// During page rendering:
<input type="hidden" name="my_token" id="my_token" value="<? php echo $_SESSION['my_token']?>" />

// After they click submit, when checking form:
if ($_POST['my_token'] === $_SESSION['my_token'])
{
        // was ok
}
else
{
          // was bad!!!
}

and because it is "per form" - you wont get double form submissions - because you can wipe the token after the first form submission!

并且因为它是“按表单” - 您不会获得两次表单提交 - 因为您可以在第一次提交表单后擦除令牌!

回答by Gumbo

Generally, it suffices to have one single token per user or per session. It is important that the token is bound to just one particular user/session and not globally used.

通常,每个用户或每个会话拥有一个令牌就足够了。令牌仅绑定到一个特定的用户/会话而不是全局使用,这一点很重要。

If you are afraid the token could get leaked to or get obtained by an attacking site (e.?g. by XSS), you can limit the token's validity to just a certain timeframe, a certain form/URL, or a certain amount of uses.

如果您担心令牌可能会泄漏到攻击站点或被攻击站点(例如通过 XSS)获取,您可以将令牌的有效性限制在特定时间范围、特定形式/URL 或特定数量的使用。

But limiting the validity has the drawback that it might result in false positives and thus restrictions in usability, e.?g. a legitimate request might use a token that just have been invalidated as the token request is too long ago or the token has already been used too often.

但是限制有效性的缺点是它可能会导致误报,从而限制可用性,例如。一个合法的请求可能会使用一个刚刚失效的令牌,因为令牌请求太久以前或者令牌已经被使用得太频繁了。

So my recommendation is to just use one token per user/session. And if you want further security, use one token per form/URL per user/session so that if a token for one form/URL gets leaked the others are still safe.

所以我的建议是每个用户/会话只使用一个令牌。如果您想要进一步的安全性,请为每个用户/会话的每个表单/URL 使用一个令牌,这样如果一个表单/URL 的令牌被泄露,其他的仍然是安全的。

回答by hakre

The answer to your question is: it depends.

你的问题的答案是:这取决于。

And you don't need to use session for timed tokens, you can just use the server-time and a secret key on the server.

而且您不需要将会话用于定时令牌,您可以只使用服务器时间和服务器上的密钥。

But let's say it's better to generate a token each hour, then I would need two sessions: token, expiration,

但是让我们说最好每小时生成一个令牌,然后我需要两个会话:令牌,到期,

No, you need a routine that is able to generate a token for a time-frame. Let's say you divide time per 30 minutes. The you create one token for the current 30 minutes in the form.

不,您需要一个能够为某个时间范围生成令牌的例程。假设您每 30 分钟分配一次时间。您在表单中为当前 30 分钟创建一个令牌。

When then form is submitted and you verify the token against for now and against the previous 30 minute period. Therefore a token is valid for 30 minutes up to one hour.

然后提交表单,您针对现在和前 30 分钟的时间段验证令牌。因此,令牌的有效期为 30 分钟至一小时。

$token = function($tick = 0) use($secret, $hash) {
    $segment = ((int) ($_SERVER['REQUEST_TIME'] / 1800)) + $tick;
    return $hash($secret . $segment);
};

回答by Cata Cata

You can use both methods .

您可以使用这两种方法。

1) A random token for each request . And to solve problem of not syncronised tokens , you can use server-sent events http://www.w3.org/TR/eventsource/or websockets http://www.w3.org/TR/websockets/comunication technology to update the tokens in real time for every page.

1) 每个请求的随机令牌。并解决令牌不同步的问题,您可以使用服务器发送的事件http://www.w3.org/TR/eventsource/或 websockets http://www.w3.org/TR/websockets/通信技术来更新每个页面的实时令牌。

2) You can use a token per user session (no need to do it per hour) that is easyer to implement . You wont have problem with syncronisation but if the token is not too strong it can be guessed . Ofcorse if the token is very random it will be very hard for malentended person to actually do a request forgery .

2)您可以为每个用户会话使用一个令牌(无需每小时执行一次),这更容易实现。你不会有同步问题,但如果令牌不是太强,它可以被猜到。Ofcorse 如果令牌非常随机,那么受害人将很难真正进行请求伪造。

P.S. first method is the most secure but its harder to implement and uses more resources .

PS 第一种方法是最安全的,但它更难实现并使用更多资源。