Question

I want to know if this code is strong enough to prevent CSRF attack on PHP Form?

<?php
session_start();
session_regenerate_id(true);

if (isset($_POST['submit'])) {
if (isset($_SESSION['token']) && ($_POST['token'] == $_SESSION['token'])) {
}
}
$token = hash('sha256', uniqid(mt_rand(), true));
$_SESSION['token'] = $token;
?>

//FORM

<form method="POST" action="page.php">
<input type="hidden" name="token" value="<?php echo $token; ?>">
<input type="submit" name="submit">
</form>

Thanks.

Was it helpful?

Solution

If the victim has not viewed any forms on your site, he will not yet have a token stored in his session.

If the attacker presents the victim with a form with no token field at all, the POST request made by the victim will pass the CSRF check because $_POST['token'] and $_SESSION['token'] will both be null. (Or both empty strings depending on how PHP initialises unknown variables.)

You must also check that the token exists in the session before checking for equality and abort if either of those tests fail.

Depending on your site, a user not having seen a form may be very likely or it may be an extreme edge case. With checking for the existence of the token first, it doesn't matter how many forms you have on your website, there is no possibility of a CSRF attack.

Apart from that small problem, I can't see any CSRF vulnerability in it. That code looks like it will do the job.

OTHER TIPS

I’d say it suffices for the given purpose.

The values returned by uniqid(mt_rand(), true) should be up to 33 bytes:

  • up to 10 bytes prefix from mt_rand
  • 8 bytes system time in seconds
  • 5 bytes current microseconds
  • 10 bytes from the internal linear congruence generator php_combined_lcg

However, these 33 bytes do not provide 264 bits of entropy but way less:

  • log2(231-1) ≈ 31 bits for the mt_rand prefix
  • system time is known (e.g. Date response header field)
  • microseconds can only have one of 106 values, so log2(106) ≈ 20 bits
  • LCG value is log2(109) ≈ 30

This sums up to almost 81 unknown bits. To brute force this, one would need on average 281/2 ≈ 1.2·1024 guesses that result in a given token when hashed. The data to process would be approximately 8·1013 TB. With a todays computer, you should be able to run this in approximately 5.215·1017 seconds.

This should be sufficient to render an attack impracticable.

On the products I support, I'd say "no." Your random number generator is based on rand() which is predictable. Also, it looks like the random number is very short - it needs to be long enough that it cannot be brute forced during the session's validity - nor cany any of the many active sessions' CSRF tokens be cracked.

Check out the OWASP page on CSRF They'll give you good guidance.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top