Thursday, 21 April 2011 07:55
In today’s tutorial, we will learn how to painlessly protect your CodeIgniter (pre 2.0) application against Cross-Site Request Forgery attacks. The library we’ll be creating today will automate all of the protection mechanisms, making your site stronger and more secure.

Cross-Site Request Forgery attacks are based on unprotected forms on your sites.
An attacker could create a bogus form on his site – for example a search form. This form could have hidden inputs that contain malicious data. Now the form isn’t actually sent to the attacker’s site to perform the search; in reality, the form points to your site! Since your website will trust that the form is genuine, it goes through and executes the requested (and perhaps malicious) actions.
Imagine that a user is logged into your site, and is redirected to the attacker’s site for some reason (phishing, XSS, you name it). The attacker’s form could point to your account deletion form on your site. If the user performs a “search” on the attackers site, his account will then be deleted without him knowing!
There are numerous ways to prevent these sorts of attacks.
POST request, compare the submitted token to the one on store, and, if they differ, deny the request. Your site still needs to be protected against XSS though, because if it’s not, this method becomes useless.
We’ll need to do three things for each request:
POST request, validate that the submitted token. To do this automatically, we’ll use CodeIgniter hooks. Hooks allow us to execute all actions on different parts of the request. We’ll need three:
Let’s get started. We’ll go step by step in order to explain everything as thoroughly as possible. We’ll create the method that generates the token first, so we can test everything correctly afterwards. Create a file in your system/application/hooks folder called “csrf.php“, and paste the following code:
<?php
/**
* CSRF Protection Class
*/
class CSRF_Protection
{
/**
* Holds CI instance
*
* @var CI instance
*/
private $CI;
/**
* Name used to store token on session
*
* @var string
*/
private static $token_name = 'li_token';
/**
* Stores the token
*
* @var string
*/
private static $token;
// -----------------------------------------------------------------------------------
public function __construct()
{
$this->CI =& get_instance();
}
}
Hopefully, what we’ve added above should look rather basic to you. We’re creating a class, called CSRF_Protection, an instance variable to hold the CodeIgniter instance, two static variables to hold the name of the parameter that will store the token, and one to store the token itself for easy access throughout the class. Within the class constructor (__construct), we simply retrieve the CodeIgniter instance, and store it in our corresponding instance variable.
Note: The “name of the parameter” is the name of the field that holds the token. So on the forms, it’s the name of the hidden input.
After the class constructor, paste the following code:
/**
* Generates a CSRF token and stores it on session. Only one token per session is generated.
* This must be tied to a post-controller hook, and before the hook
* that calls the inject_tokens method().
*
* @return void
* @author Ian Murray
*/
public function generate_token()
{
// Load session library if not loaded
$this->CI->load->library('session');
if ($this->CI->session->userdata(self::$token_name) === FALSE)
{
// Generate a token and store it on session, since old one appears to have expired.
self::$token = md5(uniqid() . microtime() . rand());
$this->CI->session->set_userdata(self::$token_name, self::$token);
}
else
{
// Set it to local variable for easy access
self::$token = $this->CI->session->userdata(self::$token_name);
}
}
Step by step:
FALSE, then the token is not yet present.
We need to ensure that the token was submitted, and is valid in case the request is a POST request. Go ahead and paste the following code into your csrf.php file:
/**
* Validates a submitted token when POST request is made.
*
* @return void
* @author Ian Murray
*/
public function validate_tokens()
{
// Is this a post request?
if ($_SERVER['REQUEST_METHOD'] == 'POST')
{
// Is the token field set and valid?
$posted_token = $this->CI->input->post(self::$token_name);
if ($posted_token === FALSE || $posted_token != $this->CI->session->userdata(self::$token_name))
{
// Invalid request, send error 400.
show_error('Request was invalid. Tokens did not match.', 400);
}
}
}
POST request, which means that a form was, in fact, submitted. We check this by taking a look at the REQUEST_METHOD within the $_SERVER super global. $this->CI->input->post(self::$token_name) is FALSE, then the token was never posted.
This is the fun part! We need to inject the tokens in all forms. To make life easier for ourselves, we are going to place two meta tags within our <head> (Rails-like). That way, we can include the token in AJAX requests as well.
Append the following code to your csrf.php file:
/**
* This injects hidden tags on all POST forms with the csrf token.
* Also injects meta headers in <head> of output (if exists) for easy access
* from JS frameworks.
*
* @return void
* @author Ian Murray
*/
public function inject_tokens()
{
$output = $this->CI->output->get_output();
// Inject into form
$output = preg_replace('/(<(form|FORM)[^>]*(method|METHOD)="(post|POST)"[^>]*>)/',
'$0<input type="hidden" name="' . self::$token_name . '" value="' . self::$token . '">',
$output);
// Inject into <head>
$output = preg_replace('/(</head>)/',
'<meta name="csrf-name" content="' . self::$token_name . '">' . "n" . '<meta name="csrf-token" content="' . self::$token . '">' . "n" . '$0',
$output);
$this->CI->output->_display($output);
}
display_override hook, we need to retrieve the generated output from CodeIgniter. We do this by using the $this->CI->output->get_output() method. POST. header (if present). This is simple, since the closing head tag should only be present once per file. display_override hook, the default method to display your view will not be called. This method includes all sorts of things, which we should not – just for the purposes of injecting some code. Calling it ourselves solves this.
Last, but not least, we need to create the hooks themselves – so our methods get called. Paste the following code into your system/application/config/hooks.php file:
// // CSRF Protection hooks, don't touch these unless you know what you're // doing. // // THE ORDER OF THESE HOOKS IS EXTREMELY IMPORTANT!! // // THIS HAS TO GO FIRST IN THE post_controller_constructor HOOK LIST. $hook['post_controller_constructor'][] = array( // Mind the "[]", this is not the only post_controller_constructor hook 'class' => 'CSRF_Protection', 'function' => 'validate_tokens', 'filename' => 'csrf.php', 'filepath' => 'hooks' ); // Generates the token (MUST HAPPEN AFTER THE VALIDATION HAS BEEN MADE, BUT BEFORE THE CONTROLLER // IS EXECUTED, OTHERWISE USER HAS NO ACCESS TO A VALID TOKEN FOR CUSTOM FORMS). $hook['post_controller_constructor'][] = array( // Mind the "[]", this is not the only post_controller_constructor hook 'class' => 'CSRF_Protection', 'function' => 'generate_token', 'filename' => 'csrf.php', 'filepath' => 'hooks' ); // This injects tokens on all forms $hook['display_override'] = array( 'class' => 'CSRF_Protection', 'function' => 'inject_tokens', 'filename' => 'csrf.php', 'filepath' => 'hooks' );
post_controller_constructor hook, so we need to add those brackets (“[]“). Refer to the documentation on CodeIgniter hooks for more info. post_controller_constructor, generates the token in case it hasn’t been generated yet. form and the header. With minimal effort, we’ve built quite a nice library for ourselves.
You can use this library in any project, and it will automagically protect your site against CSRF.
If you’d like to contribute to this little project, please leave a comment below, or fork the project on GitHub. Alternatively, as of CodeIgniter v2.0, protection against CSRF attacks is now built into the framework!