Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

is this all i need to do to prevent csrf attacks with php and ajax?

Tags:

ajax

php

csrf

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?

like image 873
stergosz Avatar asked Dec 12 '22 04:12

stergosz


1 Answers

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:

Cookies won't protect you from CSRF

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:

  • multiple actions at the time
  • flood of request for new security token
  • how long to let the token live

Solution

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:

  • if you'll request security token for the same action within 15 minutes you'll get the same token
  • you'll get unique token for each action
  • if you'll set token expiration for 4 hours, token will be active from 3:45 to 4:00
  • if attacker try to send you 200000 token requests in one minute you'll still have just one row in database
  • each user will have maximally 16 records in the table at once

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:

  • attacker requests MANY tokens
  • user will write article for several hours
  • if you have a table with delete buttons for hundreds of articles

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 :)

like image 155
Vyktor Avatar answered Feb 15 '23 09:02

Vyktor