If I want to protect my site and users from Cross Site Forgery (CSRF) attacks, I can generate a unique token $token = md5( time() * rand );
on every page that has a form. The token is is submitted in a hidden input field echo '<input type="hidden" name="token" value="'.$token.'">';
and at the same time stored in a session variable $_SESSION['token'] = $token;
.
I will check if on any submitted form if($_POST['token'] == $_SESSION['token'])
and proceed accordingly.
However some users may multitask. Which is something that I am actually doing right now, while I am posting this.
While composing my post I open different windows / tabs to possibly research information or look at some other questions on stack overflow. Stack overflow lets me submit the form with no problems.
But if I were to do that on my site doing this - meaning browse other pages while still composing a post/form - my $token
would be regenerated each time I pull up a different page from my website. Making the hidden input
token on the form I am working on and eventually want to submit incorrect, because it wont match the $_SESSION['token']
variable anymore, which has been regenerated when I visited a different page...
Any good ideas how to prevent this issue, or any better solutions to stop CSRF in the first place?
I want to allow my users to multi task AND want to be protected against CSRF...
I've had the same problem with what you state because of single CSRF and it gets replaced unless they submit the latest page, but if you use a array w/session it should solve your problem(s). Also you might want to include a captcha, I'd recommend Google's 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
}
}
}
your HTML:
<input type="hidden" name="token" value="<?php createToken(); ?>">
PHP Token Checker
if(isset($_POST['token']) && checkToken($_POST['token'])){
#valid token
}else{
#create error message saying that they tried to repost data or session token expired
}