Home » Php » php – New CSRF token per request or NOT?

php – New CSRF token per request or NOT?

Posted by: admin April 23, 2020 Leave a comment

Questions:

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?

$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?

How to&Answers:

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

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.

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.

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.

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

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.

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!

Answer:

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.

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.

Answer:

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.

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.

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

Answer:

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.

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 .

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