CSRF Protection in CodeIgniter 2.0: A closer look
Cross Site Request Forgery (CSRF) is one of the most common vulnerabilities in websites and web applications. As announced, CodeIgniter 2.0 will feature a built-in CSRF protection, which I’d like to analyze for you.
In this year’s Top 10 of the Most Critical Security Risks in Web Applications, which is issued by the OWASP, CSRF ranks fifth, a rank which it has had for more than four years now. For a reason – its rather high complexity, multitude of attack vectors and massive damage potential make it one of the most dangerous attacks on websites. Developers often tend to underestimate the problem or don’t know how to implement an effective protection.
In this blog post, I’m going to analyze such a protection and show you how CodeIgniter 2.0 tackles the issue. However before I go into detail, I’m going to give you a quick refresher about CSRF and how it works. This might be useful to those of you who don’t deal with web security on a daily basis. If you know CSRF by heart already, you might as well skip this section.
CSRF explained
What is CSRF and how does it work? CSRF, also known as XSRF, is short for Cross Site Request Forgery. OWASP’s definition for CSRF is this:
A CSRF attack forces a logged-on victim’s browser to send a forged HTTP request, including the victim’s session cookie and any other automatically included authentication information, to a vulnerable web application. This allows the attacker to force the victim’s browser to generate requests the vulnerable application thinks are legitimate requests from the victim.”
So, what we need to perform a CSRF attack is this:
- A vulnerable website or application
- A victim which is logged in at this website
Let’s assume the vulnerable website www.example.com allows users to buy goods from their web shop. For the sake of simplicity, I’m using GET parameters to demonstrate the problem, however POST is affected likewise. The URL which a logged-in user has to visit to buy a product is the following:
http://www.example.com/buy.php?productID=x&amount=y
By calling this URL, we submit the parameters productID and amount with the values x and y to the script. The script uses those parameters to process the order. A malicious attacker can now make a victim’s browser send a request to this website. This could be done by simply putting an image tag on a website that he controls:
<img src="http://www.example.com/buy.php?produktID=20&menge=1000" />
The victim’s browser doesn’t know that the referenced URL is not an image at all; it just sends a HTTP request to the given URL to retrieve whatever data there is. And here’s the trick: Because the victim is logged in at example.com, the browser sends all of the victim’s session and authorization data with the request. The victim has unknowingly sent a request to buy 1000 pieces of the product to example.com, and the website has no idea that the request is illegitimate – the order would be executed.
Principles of CSRF protection
So, how do we protect a website against CSRF attacks? The underlying principle is easy. A CSRF attack is based on the fact that the attacked website has no way of knowing if the data it receives actually came from a form on this website. What we need is a way to connect the two necessary HTTP requests – form request and form submission – so that we get this piece of information. We can then make sure that data we receive was really entered by a user on our website.
There are several ways to do this. The most common one, which is also used by CodeIgniter, includes a hidden field in each form on the website. This hidden field is called CSRF token. The CSRF token is a random value that changes with each HTTP request sent. As soon as it is inserted in the website forms, it gets saved in the user’s session as well. When the form is submitted, the website checks if the submitted CSRF token equals the one saved in the session. If so, the request is legitimate. The token changes each time a page is requested, which means an attacker would have to guess the current token to successfully perform a CSRF attack.
CSRF Protection in CodeIgniter 2.0
Let me show you how CodeIgniter implements the protection. To enable it, you have to set the correspondig config value to TRUE:
$config['csrf_protection'] = TRUE;
The Security class generates a unique value for the CSRF token with each HTTP request. When the object is created, the name and value of the token are set.
// Append application specific cookie prefix to token name $this->csrf_cookie_name = $this->csrf_token_name; // Set the CSRF hash $this->_csrf_set_hash();
The function for it is this:
function _csrf_set_hash()
{
if ($this->csrf_hash == '')
{
// If the cookie exists we will use it's value. We don't
// necessarily want to regenerate it with
// each page load since a page could contain embedded
// sub-pages causing this feature to fail
if ( isset($_COOKIE[$this->csrf_cookie_name] ) AND
$_COOKIE[$this->csrf_cookie_name] != '' )
{
$this->csrf_hash = $_COOKIE[$this->csrf_cookie_name];
} else {
$this->csrf_hash = md5(uniqid(rand(), TRUE));
}
}
return $this->csrf_hash;
}
The function first checks the cookie’s existence. If it does exist, its current value is used. The reason is also in the code: If the security class is instantiated multiple times, each request would overwrite the previous one. This would cause only the last generated form to work. Such a case can occur when multiple sub-pages are used in one page, or when loading a form via AJAX.
The function creates a globally available hash value and saves it for further processing – the token’s value is generated. Now it has to be inserted in every form on the website. To do this, the function form_open(), which is part of the form helper, is used:
function form_open($action = '', $attributes = '', $hidden = array())
{
[...]
// CSRF
if ($CI->config->item('csrf_protection') === TRUE)
{
$hidden[$CI->security->csrf_token_name] =
$CI->security->csrf_hash;
}
if (is_array($hidden) AND count($hidden) > 0)
{
$form .= sprintf("\n%s",form_hidden($hidden));
}
return $form;
}
This function now not only creates the opening form tag, but also automatically appends the CSRF token to the form. This means that in order to implement a complete CSRF protection, you always have to use form_open() when creating forms. The token is now set and gets submitted with each form. After submission, it has to be checked and compared to the value saved in the session. This is done by the input class during input processing:
// CSRF Protection check
if ($this->_enable_csrf == TRUE)
{
$this->security->csrf_verify();
}
The method csrf_verify() of the Security class is called each time a form is sent. This is where the actual check happens:
function csrf_verify()
{
// If no POST data exists we will set the CSRF cookie
if (count($_POST) == 0)
{
return $this->csrf_set_cookie();
}
// Do the tokens exist in both the _POST and _COOKIE arrays?
if ( ! isset($_POST[$this->csrf_token_name]) OR
! isset($_COOKIE[$this->csrf_cookie_name]) )
{
$this->csrf_show_error();
}
// Do the tokens match?
if ( $_POST[$this->csrf_token_name]
!= $_COOKIE[$this->csrf_cookie_name] )
{
$this->csrf_show_error();
}
// We kill this since we're done and we don't
// want to polute the _POST array
unset($_POST[$this->csrf_token_name]);
// Re-generate CSRF Token and Cookie
unset($_COOKIE[$this->csrf_cookie_name]);
$this->_csrf_set_hash();
$this->csrf_set_cookie();
log_message('debug', "CSRF token verified ");
}
The csrf_verify() method does two things. If no POST data is received, the CSRF cookie is set. If POST data is received, it checks if the submitted value corresponds with the CSRF token value in the session. If this is the case, the CSRF token's value is deleted and generated again for the next request. The request is recognized as legitimate and the circle can start again.
Conclusion
Finally, finally, finally! With this improvement, it’s now really easy to implement a reliable CSRF protection. All you need to do is set the config value to TRUE and CodeIgniter does the rest for you. The fact that you can keep on using the usual methods and don’t have to remember doing anything in addition make this a really smart implementation.
If you’re not using CodeIgniter 2.0 yet and don’t plan on doing so for a while, you might still consider copying the CSRF protection methods from the 2.0 source code and update your 1.7.2 installation with it – this will give you a big gain in security.
Read on
Security-Guru Chris Shiflett about CSRF and CSRF protection
PDF: Top 10 Security Risks in Web Applications (OWASP)
Comments (26)
Michael Reichner
2010-12-20, 18:54Great post, Bastian.
I found this while trying to remediate AJAX issues on one of my sites. I wrote about my experience and solution here:
http://aymsystems.com/ajax-csrf-protection-codeigniter-20
Prasanna
2011-03-05, 06:53Does help handle post backs? i.e Page reloads?
AE
2011-04-20, 09:19Thank you! This was a veru useful post for me!
Vimson
2011-06-15, 19:26I would like to eallow some domains to post data using CURL to my website for implementing a
service. Can you please give me a solution for avoiding this for some specified IPs or domain names
ab
2011-07-24, 23:50Hi there, enabled csrf_protection doesnt work with Facebook application. Do you know any solution for that?
wjtd
2011-09-26, 15:41Unfortunately, this only works if you have one form on a page... if you have multiple forms (i.e. two or more functional buttons that must have different forms surrounding them) the CSRF functions in CI cause a validation error in that the csrf "id" is duplicated.
Nic Rosental
2011-10-17, 21:14Thanks! That's a very nice rundown on the magic behind CSRF protection in CI 2. Indeed it is extremely easy to implement and I should've been doing it for a while now.
Dennis
2011-10-25, 13:03Hey Bastian,
ich schaue mir gerade CodeIgniter ein wenig an. Ich suche aktuell gerade nach einem Tutorial wie einfache CRUD-Szenarien einfach und schnell umgesetzt werden. Ich finde allerdings nur Beispiele die auf dem alten validation-Helper basieren.
Naja, so bin ich jedenfalls hier gelandet. ;-) Sehr informativer Artikel! Komisch finde ich, dass CI den Schutz standardmäßig deaktiviert hat. Siehst du da einen Grund für?
BnoL
2011-11-30, 22:39Nice. But I wonder why CI doesn't store the csrf_hash in a more secure way, such as session or at least a http-only cookie since (normal) cookie can be stolen via XSS attack.
alice01
2013-01-30, 03:16http://khaokrapook.com/webboard/reply.php?board_id=00227
K Kiran
2012-06-15, 11:33Dear Bastian,
The CI CSRF Token is not getting saved in the session, then how there can be assurance that it is safe from CSRF attacks when a form is submitted? Can you please elaborate?
Josh Highland
2012-07-22, 06:02Great read. Thank you for explaining it so clearly.
Josh Highland
2012-07-22, 06:03Great read. Thank you for breaking down why CSRF is imporant and explaining it so clearly. After I post this, I'm updating my config file!
Centurion
2012-01-08, 21:05I also took a look at Security class I don't get how "csrf_verify()" does actual verification. In the function "_csrf_set_hash()", the generated hash value is just stored in class member "_csrf_hash". However, according to examples on internet, the generated value should be stored in $_SESSION. When you submit a form with a hidden token and if that token was not saved in $_SESSION previously then how you will check if sent token matches generated one? Currently "csrf_verify" function just checks if "$_POST[$this->_csrf_token_name]" and "$_COOKIE[$this->_csrf_cookie_name]" are set and are not empty, BUT both might be sent by the evil site! Why they are not saving generated token in SESSION and not performing check like here: http://codeutopia.net/blog/2008/10/16/how-to-csrf-protect-all-your-forms/
arjun
2012-03-19, 11:17if i put more then one form in the same page , how can i handle CSRF token value !
Peeyush Chandel
2012-03-27, 06:30very nice stuff..nicely explained..thanks
kabel
2013-02-17, 21:36And what about method GET? How is controlled this method? Thanks
Cheap Snapbacks
2013-03-31, 08:38Cheap Snapbacks
Cheap Snapbacks
2013-03-31, 08:39I want to introduce the interesting website as your article.
Cheap Snapbacks
2013-03-31, 08:40I want to introduce the interesting website as your article.You can find the useful information about Cheap Snapbacks,as a kind of Snapback Hats, the website have enough article about these hats.
White Collar Season 4 DVD
2013-04-25, 09:55The idea intends to offer coders with the ability to create effective PHP applications with advanced features. It is somewhat simple and easy to perform on for developers.Keep Sharing...
Amy Watson
2013-04-29, 13:16I would like to say, it's quite informative post. According to me Cross Site Request Forgery
(CSRF) is really one of major thing to focus on in web application as mentioned by you also. CodeIgniter 2.0 is having this feature buit-in protection which everyone should know.
New Era Snapbacks
2013-05-08, 05:30I must say, you have explained in such a good way, Cross Site Request Forgery protection in CodeIgniter version 2 is such an amazing thing and provides various benefits and simplicity when comes to HTTP GET and POST request response methods. So one must hire CodeIgniter developers who really knows this.
Rafael Duarte
2012-09-15, 03:47Congrats... really good stuff...
after reading this article a took a good look at csrf of the code igniter... but then i had a dark thought... i used jquery to make an ajax request to a page with a form, appended the code in a hidden div of my attacking page, then retrived the tolken from the form... all this allow me to dinamicaly retrieve the tolken and embed in my request... i'm not sure if that would be a problem or if a .htaccess or diferent domains itself would prevent this... so i decided to ask you guys... thank you very much...
Cheryl Ray
2012-09-26, 11:34I must say, you have explained in such a good way, Cross Site Request Forgery protection in CodeIgniter version 2 is such an amazing thing and provides various benefits and simplicity when comes to HTTP GET and POST request response methods. So one must hire CodeIgniter developers who really knows this.
Chandler Berger
2012-10-06, 10:37CodeIgniter 2.0 is based on Model View Controller (MVC) design. The idea intends to offer coders with the ability to create effective PHP applications with advanced features. It is somewhat simple and easy to perform on for developers.Keep Sharing... Thanks!