I have been looking at CodeIgniter lately mainly because a lot of my colleagues are using it already. As with most frameworks, I usually start using the framework in a project and then as I am developing the application, I notice some issues with the framework. One such case is with CodeIgniter’s built-in XSS protection. This functionality is quite nice (at least they included it in the framework) but there are some issues with using it that developers should be aware of.
Setting it all up
From CodeIgniter’s documentation (see http://codeigniter.com/user_guide/libraries/input.html)…
CodeIgniter comes with a Cross Site Scripting Hack prevention filter which can either run automatically to filter all POST and COOKIE data that is encountered, or you can run it on a per item basis. By default it does not run globally since it requires a bit of processing overhead, and since you may not need it in all cases.
The XSS filter looks for commonly used techniques to trigger Javascript or other types of code that attempt to hijack cookies or do other malicious things. If anything disallowed is encountered it is rendered safe by converting the data to character entities.
XSS Protection functionality is turned off by default. To turn on this functionality, you simply either set the global configuration (i.e. system/application/config/config.php)…
$config['global_xss_filtering'] = TRUE;
…or, you can do it manually within your controller(s) like so…
$myvar = $this->input->xss_clean($data);
How CodeIgniter’s XSS Protections Work
Once set up, you can now start to see what happens when you pass parameter values to your application. For example, lets assume we have a controller named HelloWorld which is coded like so…
class HelloWorld {
public function index() {
$myvar = $this->input->post(“myvar”);
$this->load->view(“myview”, array(“myvar” => $myvar));
}
}
A simple controller by any taste. So, the view is just as simple…
Hello World.<br />
<?php echo $myvar; ?>
If we supply a post parameter to our call to HelloWorld, the view will output the value we supply it. This is good way to test if the XSS filtering is working.
So, what happens when CodeIgniter knows that the XSS Protections are enabled and receives input from the client/browser? Well, as part of the Controller instantiation, the [CI_]Input class is loaded. The [CI_]Input class will firstly load the config.php file and see what settings need to be set then it will call the _sanitize_globals() private function. This function will setup everything and I will not get too deep into it other than to explain that it sets up the inputs into the application, including $_GET, $_POST, $_SERVER, and $_COOKIE as well as others. For each one of these constructs, it will call _clean_input_data() and xss_clean(). The function xss_clean() has the following code comments…
Sanitizes data so that Cross Site Scripting Hacks can be prevented. This function does a fair amount of work but it is extremely thorough, designed to prevent even the most obscure XSS attempts. Nothing is ever 100% foolproof, of course, but I haven’t been able to get anything passed the filter.
How cute. The developer goes on to record that he used the XSS Cheatsheet from ha.ckers.org. It would be nice if they actually read the material rather than simply looked over the various methods to get past various XSS filters listed on the page. Because, in 5mins I was able to get past this filter. I will show you why later.
Ok, to continue … xss_clean() will firstly remove any invisible characters like NULL terminators which could cause HTTP Parameter Pollution and/or HTTP Response Splitting issues. This is a good first step. The function continues on to decode URLencoded values, convert attributes and entities, remove invisible characters (again), and then…the magic. It will look for a standard list of elements, JS DOM Calls, etc. and then replace any found with the string “[removed]“.
The Issue(s)
The problems are: (1) this functionality is not turned on by default and (2) the authors of CodeIgniter decided to use a substitution method without the use of encoding before hand. I will focus on the latter issue here.
The age-old method of substitution to prevent XSS in web applications goes waaaayyyy back. MySpace and Yahoo! both used this methodology to protect their users from XSS attacks as well. The key to the substitution method is to be able to read the input to be able to convert it before outputting. But, like so many, most developers switched to a better method — encode-and-validate instead of find-and-replace.
The reason is simple: for web applications, there are literally a thousand ways to send a single object to the web server. With many, many different character sets (i.e. UTF-7) and encoding methods (i.e. Base64, Rot-13, URLEncoded, HTMLEntities, etc.) available to the user, it is really, really easy for a developer to miss out on one.
CodeIgniter developer(s) thought that by looking for common strings, they could write a XSS filter which replaces those common strings used in attacks to “removed”. Unfortunately, they missed out on Javascript events. So, a value for $myvar of fdsa” onload=”alert(1);” /> is possible to by-pass their protections. Yes, I know — we are not really outputting an image here to really use the onload event, but for all purposes, this should not possible.
Just what is the problem now?
The following results were found while testing various inputs to CodeIgniter (using the HelloWorld controller and view above)…
- Input: fdsa” onload=”alert(1);” />, Output: fdsa” onload=”alert(1);” />
- Input: fdsa<script>alert(1)</script>, Output: fdsa[removed]alert(1)[removed]
- Input: fafa<script src=”http://ha.ckers.org/xss.html”>alert(1)</script>, Output: fdsa[removed]alert(1)[removed]
- Input: fdsa<img src=”…” onload=”alert(1);” />, Output: fdsa<img />
So, from the above tests, we can see that (1) CodeIgniter misses Javascript events when not within a HTML tag, (2) does not always strip the actual Javascript code, and (3) it only strips the attributes from normal HTML tags (i.e. IMG) it finds.
This means that the developer using CodeIgniter needs to remember these limitations or they can lead to a security issue. For example, if your view was to output an IMG tag or something based on the user’s input, you may introduce a XSS vuln to your application if you do not perform some additional encoding (and preferable validation as well).
What You Should Remember if Using CodeIgniter
You should always: (1) ENCODE and then (2) VALIDATE your inputs. Encoding can be done using the combination of the XSS protections already in CodeIgniter, but you should also use something like urlencode() or htmlentities() just to make sure. So, we can change our example HelloWorld controller to do just that…
class HelloWorld {
public function index() {
$myvar = urlencode($this->input->post(“myvar”));
$this->load->view(“myview”, array(“myvar” => $myvar));
}
}
By using encoding on top of the XSS protections within CodeIgniter, you can protect yourself against any limitations that CodeIgniter may have. You should additionally validate any data by data-typing (if possible) or if using strings, use a dictionary-lookup or a regular-expression check.


Nice post, I really enjoyed it
Thanks Lars.
Why would you want it switched globally by default? I have loads of pages which won’t output any of the userdata (session/cookie/post) and won’t save it to anywhere. Having it switched on by default is just lazy and adds unnecessary overhead as the User Guide says.
Also on my backend I need the user to be able to submit all javascript and html, no limitations. If I were to switch on global XSS filtering that would be impossible.
For the most part I found your article very informative and agree that the filter could be improved. But switching XSS filtering on globally is just lazy programming, protect what needs protecting and use type casting to protect ints/doubles but don’t start filtering everything.
In small (single-purpose) applications it would be fine though, but in any larger projects you’ll run into a wall if you want to allow admins to add javascript, images or flash.
Firstly, thanks for reading Jelmer. Secondly, security always comes at a cost. Well, what doesn’t really? Thirdly, validation is discussed using a couple of methods, one of which is data-typing as you mention. You cannot rely on data-typing for strings though — which from your description is a lot of your inputs because you are accepting markup and scripting code directly from the user. For strings, one must use regular expressions or dictionary lookups (which would not be likely used in your application much).
All inputs for web applications are inherently string by default. When you use intval, floatval, boolval, or similar functions, PHP is having to parse the strings. There is an overhead right there that I am sure you have not really considered. My point about the overhead is this: why would you skip on security when you clearly have overhead in other areas which are so much less important? Hell, PHP itself is an overhead really.
This article discusses issues with the XSS protections within CodeIgniter. The aim is to educate developers using CodeIgniter about the XSS protections and how these protections do not go far enough to protect your application from XSS and other attack vectors. It does not state anywhere you have to turn anything on, but this is most certainly advised. It is up to you (as the developer) to turn on the XSS protections or not, but this is definitely not a lazy step to take.
Turning the feature on globally ensures that all inputs are some-what sanitized before you ingest them within your application. If you leave the feature off (the default setting), you (the developer) are now completely in control of the security protections within your applications/controllers. Are you sure you are FULLY encoding AND validating all of the inputs within ALL of your controllers? How about the outputs? How about the server variables/headers like SERVER_NAME which can be user controlled? Do you know of every possible method to input the data you are expecting from the client (e.g. UTF-7, UTF-16, urlencoded, SQL statements/tables/etc, hidden form fields, extraneously added parameters, files, etc.)? How about the work flow of your application? Can the work flow be used to against your application?
You cannot fully trust any one security control to completely protect your web application or parts of it. For example, you assume that just because there is a login for the admins, no one else can login and upload content. Are your passwords properly encrypted in storage? Are your passwords all 14+ characters in length and follow NIST/NSA standards? Are all of your users using SSL? What about forgot your password functionalities? Are they secure enough not to allow someone to lock someone else’s account or get access to someone’s information. Probably not. It wouldn’t matter anyway — it is just one control. Like a single brick in the castle wall, it is nothing without other controls. The CodeIgniter XSS protections are yet another brick in the castle wall, but they are weak by themselves. You will need to provide some additional controls or code to ensure you are protected as much as you can be.
Remember this: most developers are not trained in proper application security techniques. If you are a web application developer and have not heard about XSS, SQLi, HRS, or any other of the top-10 OWASP vulnerabilities, I highly suggest you visit the OWASP website for more information. There really is no excuse anymore not to protect your applications from the beginning using a library or built-in protections. Anything less is … just lazy.
Some links for more information:
http://www.owasp.org/index.php/Cross-site_Scripting_%28XSS%29
http://www.owasp.org/index.php/Category:OWASP_Top_Ten_Project
http://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet
Have you report to CI developer Mike?
I’m utilize CI on my apps everyday and I surprise with your article.
But e-mail address can’t pass this protection…
Nice post.
Learned lot about xss in general and with codeigniter.
Thanks
Thanks for this! I went ahead and extended the input class to automatically do this for me:
class MY_Input extends CI_Input
{
……..
function _clean_input_data($str)
{
return htmlentities(parent::_clean_input_data($str));
}
}
Does that seem legit?