I am using Codeigniter
and i enabled CSRF
via its config.php
file...
$config['csrf_protection'] = TRUE;
$config['csrf_token_name'] = 'csrf_token_name';
$config['csrf_cookie_name'] = 'csrf_cookie_name';
then on my ajax requests i get the cookie name
var cct = $.cookie('csrf_cookie_name');
and on parameters
:
csrf_token_name : cct
My question: Do i need to do anything else or that's it?
Okay, most common case (or easiest to implement) of CSRF is something like this:
<img src="http://bank.example.com/withdraw?account=bob&amount=1000000&for=Fred" />
So if you're assuming that you're logged into bank.example.com
you're cookies are "alive" and will be send with request, so request will do what attacker want it to, so:
What can you do:
Send as many request via POST
as you can (without bothering the user), especially edits, creations and deletion. It's easier to hide security into input type='hidden'
than into URL.
Check referrer (yeah, this little thing prevents you from almost every CSRF attack from external sites):
$url = parse_url( isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '');
if( isset( $url['host']) && ($url['host'] == $_SERVER['SERVER_NAME'])){
// Request have a go
}
Add temporary security tokens to URLs like this /article/delete/25/axdefm
...
If you've spent some time generating nice URLs you'll be send because this will just screw them up and this brings on some problems such as:
You may create table for security tokens, such as this one:
tokens (
id INT,
user_id INT,
created DATETIME,
expires DATETIME, -- One of those two should be enough
value CHAR(30),
PRIMARY (id),
FOREIGN KEY (user_id) ...
);
And when some action requires authorization token you'll load last one from DB or create new one, let's say you will create new token only if all available tokes are older than 15 minutes:
function getToken( $userId, $eventIdentificator){
// Hope this is self explanatory :)
$db->deleteExpiredTokens();
// Try to load token newer than 15 minutes
$row = $db->fetchRow( 'SELECT value FROM tokens WHERE created >= ADD_INTERVAL( NOW(), -15 MINUTES) AND user_id = ?', array( $userId));
// createToken will return `value` directly
if( !$row){
$row = createNewToken( $userId);
} else {
$row = $row['value'];
}
// Real token will be created as sha1( 'abacd' . 'delete_article_8');
return sha1( $row . $eventIdentificator);
}
echo 'url?token=' . getToken( $userId, 'delete_article_' . $article['id']);
How will this act:
How to check token?
function checkToken( $userId, $eventIdentificator, $token){
$db->deleteExpiredTokens();
// Just find matching token with brute force
$rs = $db->fetch_rowset( 'SELECT value FROM tokens WHERE created >= ADD_INTERVAL( NOW(), -15 MINUTES) AND user_id = ?', array( $userId));
while( $row = $rs->fetch_row){
if( $token == sha1( $row['value'] . $eventIdentificator)){
return true;
}
}
return false;
}
If you wan't to make sure that action won't happen twice (such as edit article, this works fine for deletion) just add revision_number
or something similar to your $eventIdentificator
).
Try to think what will happen if:
I'd go with mentioned token system, it feels like a balanced solution between user comfort/implementation complexity and security, comments with ideas and notes are expected :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With