Elliott Brueggeman - PHP and Web Development Info, Photography, and More
 
Home | Web Dev Blog & Articles | PHPGraphLib | PHPSimpleChat | SkinnyTip | PHPWeatherLib | Photography | Contact
Posted on December 8, 2008 in MySQL, PHP by Elliott BrueggemanNo Comments »

Security is an important part of PHP programming, and PHP provides several tools for securing database queries and HTML display. However, knowing which function to use and when to use it can be somewhat confusing, as there’s many details to pay attention to. It’s important not to leave your website open to cross-site scripting or SQL injection attacks.

This example and explanation will focus on a common web scenario – a user submitting data via a form, being asked to confirm it, and then being displayed the data after it is stored in the database.

1. Initial User Input

In this scenario, a user will be presented an input box for which they can add a review about a particular product on a public facing website. The user is not allowed to use html, similar to the restrictions on product reviews on Amazon.com. The user inserts their review into the input box and then clicks submit.

2. User Input Preview and Cross Site Scripting Prevention

Now, we want to show the user their review and have them confirm it before we add it to the database. This is a prime example of a situation that leaves a website open for a XSS (cross-site scripting) attack. We don’t know what the user entered on the previous page, and they could be purposely entering malicious content. Because what they entered in going to be output to the screen, if they input raw PHP code, it could get executed unknowingly by our server. To prevent this, we need to html encode all potentially dangerous characters for display on the screen.

I would recommend using htmlentities() to do this, and use the ENT_QUOTES option as shown below, which means that both single and double quotes will also be encoded. Because we also decided that we wouldn’t allow HTML, we’ll want to strip that HTML before displaying the results, to give the user an accurate preview. Before doing any of this, we’ll have to grab the submitted data from the submitted form – this tutorial won’t cover this, but there are plenty of web tutorials available for doing this.

<?php
 
//strip HTML tags from input data
$input_data = strip_tags($input_data);
 
//turn all characters into their html equivalent
$preview_data = htmlentities($input_data, ENT_QUOTES);
 
//...display $preview_data 
 
?>

3. Database Insert and SQL Injection Protection

Now, let’s say the user previews above data, and clicks accept, which will send this data to another script for entry into the database. Protecting your database from SQL injection requires different steps than protecting against cross-site scripting.

It is very likely that you don’t want to store the user’s data in HTML encoded form. Let’s say you are using a varchar(32) column in your database, and you had an input box that was 32 characters long. If you were trying to store the HTML equivalent of this in your database, you would need a column that was much larger to guarantee that data didn’t get lost. This is because the HTML equivalent of a single character is often many characters long. For example, a double quote (”) becomes ", and an ampersand (&) becomes &. We need to guard against single quotes, because these can cause an SQL injection problem, depending on how your database is setup. Consider this example:

<?php
 
$name = "George'); DELETE FROM mytable; INSERT INTO mytable (name) VALUES ('you got hacked";
 
$sql = "INSERT INTO mytable (name) VALUES ('$name')";
 
//...run the $sql query
 
?>

If your SQL server allows more than one SQL command on a single query request, you’ve just lost lots of data. While it’s a good idea to lock down your database server so it doesn’t allow this, you always want to secure your code independently of the database, in case you change your hosting setup later.

To secure your script, you can use the addslashes() function which will escape both single and double quotes, by adding backslashes before them, to prevent multiple queries from being executed. You’ll also want to test for the length of the input field, as forged form requests are easy as can be using tools like Firebug. Here is example code that will accomplish that:

<?php
 
//escape trouble characters
$name = addslashes($name);
 
//make sure not longer than expected length
$name = substr($name, 0, 32);
 
$sql = "INSERT INTO mytable (name) VALUES ('$name')";
 
//...run the $sql query
 
?>

Note that there is the possibility of valid user input being cut off when being inserted into the database. If the user used all 32 characters in the input box, and a single quote was one of them, then the above code would trim off the last character. You may want to prevent this by using a 28 max character input box for a varchar(32) column, giving the user 4 opportunities to use an escaped character without having their input cut off.

4. Retrieving and Displaying the Data From the Database

Now that the data is safely in the database, everything is safe right? Wrong. We elected not to store HTML encoded data from users. This means that we are still at risk from a Cross Site Scripting attack every time we query and then display the data. What you’ll probably want to do is create a function that sanitizes database data before being displayed on the screen. No only should you HTML encode the data, but you’ll also want to remove the backslashes you added earlier, as these aren’t meant to be displayed. Here’s what your function could look like:

<?php 
 
function sanitize_data($input_data) {
  return htmlentities(stripslashes($input_data), ENT_QUOTES);
}
 
?>