I have an action in my site:
http://mysite.com/User/Logout
This will log the current user out of his/her session. Since this is a simple GET request, a malicious user could either create links to this page or even put this link in an开发者_C百科 image's src
attribute that would force users to get logged out. I would still like to maintain the simplicity of the logout link without having to go too far, but at the same time I would like to be able to prevent the above scenario from occurring.
Any ideas?
Well, there are a few options that you can do to help secure against CSRF attacks:
Use a form and a random token. So instead of having a "link", use a random token that's set in the session into a form
<form action="/User/logout" method="post"> <submit name="logout" value="Logout" /> <input type="hidden" name="token" value="<?php echo getSessionToken(); ?>" /> </form>
Note that POST is best for this type of action, but you could change the form to a GET without too much trouble.
Then in php, just do:
if (getSessionToken(true) != $_POST['token']) { die('CSRF!'); }
Note that
getSessionToken
should work something like this:function getSessionToken($reset = false) { if (!isset($_SESSION['random_token'])) { $_SESSION['random_token'] = sha1(uniqid(mt_rand(), true)); } $token = $_SESSION['random_token']; if ($reset) { unset($_SESSION['random_token']); } return $token; }
Also note that whenever you fetch the token to check it, you should reset it to something new (which is what this does). This prevents replay attacks where an attacker detects the token on submission and resubmits the value.
If you must use a link, then embed the token in the link. But note that this is more susceptible to attack since there's a chance the user might copy and paste the link to someone else. As long as it's a self-resetting token, there shouldn't be much issue with multiple tabs. But realize that it's not optimum:
<a href="/User/logout?token=<?php echo getSessionToken(); ?>">Logout</a>
It's absolutely better than nothing. But I would still suggest using the form for the best protection.
I would highly suggest reading and following the OWASP CSRF Guidelines for preventing CSRF. It will tell you just about all you need to know, and why...
If you log people out using GET requests (or do anything more important than that using GET requests, for that matter) then your website will be vulnerable to cross-site request forgery attacks. You should use POST to log out people. GET is only for idempotent actions, see RFC 2616 section 9.1.
Keep in mind that while using GET in this case is not compliant with the RFC, it is not everything that you need to change to prevent the XSRF. See this answer by ircmaxell for an excellent explanation.
Without changing the HTTP method you could add a unique id to the users session and append that to the logout link. If a logout action is performed and the two id's match the logout is valid. Else it's not!
Rewrite to read like/Logout?#NONCE#, #NONCE# being a value you change from request to request.
You're going to have to require some sort of validator to be passed to the logout page. And it must be soething that's unique to the session and difficult to guess. Now, if only there were a suitable value.....oh yes, the session identifier.
Note that you should be using cookies for session management, and you should be setting the http only flag on the session cookie - so you'll need to populate the link from PHP (or copy the session id into a javascript readable cookie).
<?php
session_start();
if ($_GET['logout_valid']!==session_id()) {
// handle invalid logout request
exit;
} else {
$_SESSION=array();
session_destroy();
}
I guess I am late to the party, but, here is how I handle it:
<?php
session_start();
function logoutButton($name = 'logout', $action = '/User/logout'){
$random_token = md5(uniq_id());
$_SESSION['csrf'] = $random_token;
$form = '<form ><input type="hidden" value="'. $random_token .'" name="' .$name. '"><input type="button" value="logout"><</form>';
return $form;
}
public static isValidRequest($name = 'logout'){
if(isset($_POST[$name]) && $_POST[$name] === $_SESSION['csrf']){
return true;
}
return false;
}
}
?>
to display a logout button
echo logoutButton();
To securely logout a user
if(isValidRequest()){
//logout
}
Hope it s helpful to someone.
edit: Here is a class I created https://github.com/sahithvibudhi/PHP-CSRF-Protection-class
精彩评论