Secure Your Inputs

One naivety that I often come across when having to update someone else’s PHP code is that they’ve assumed the form being posted to the script will be the form on the site that should be posting. For example, if you have a questionnaire on your website which has a list of questions and perhaps a select list or radio buttons for a set of multiple choice answers. Too many PHP developers assume that the script that will deal with processing the results is going to get that specific form submitted to it, and not one that’s virtually identical but submits from a different site, and instead of radio buttons it has a text input for each answer.

Another assumption is where the URL maybe contains a page ID to display and the developer has assumed that this page ID isn’t going to be tampered with. I’ve written in the past about Secure PHP and SQL Injections, and I’ve seen people secure the input where they know it could be a mix of alphanumeric characters and symbols, however I’ve also seen people assume that a variable should just be an integer therefore they don’t secure it.

These are both common mistakes and the wrong ones to make!

Securing Integers

If you know that a value being passed to a script should only be an integer then you can easily secure this by saving the variable and using the (int) cast. This is used as follows:

$a = (int)$_GET['pageid'];

The value of $a will then only be an integer and not contain anything else. If the pageid variable is tampered with and contains any other characters then the result will either be that $a will equal 0 or $a will equal a value providing that value is the first part of the variable. Sounds confusing doesn’t it. Let me explain by example:

$b = "I have 10 green bottles";
$a = (int)$b; // would make $a equal to nothing
$b = "10 green bottles";
$a = (int)$b; // would make $a equal to 10

So as you can see, the first version of $b contains text first so the (int) cast would make $a equal to zero. However the second version of $b has the integer at the start, so everything after this would be stripped out, leaving $a equal to 10.

Of course, if you rely on $a having a value then you need to check it still has one after the cast. Putting this into an if statement is then easily done. Let’s say for example your site uses the pageid variable to determine which page to display. If the pageid variable is zero then we’ll want the front page displaying, however we’ll also want to make sure the visitor is on the front page, so we can redirect them.

if ((int)$_GET['pageid']) :
$id = (int)$_GET['pageid'];
else :
$id = 1;
endif;

This simple statement checks to see whether the integer of the pageid variable is valid. If the result of using the (int) cast is greater than zero then the $id variable will get the pageid value saved to it, else it will get the value of 1 which is assumed to be your index page. This could also be written as:

$id = (int)$_GET['pageid'];
if (!$id) :
$id = 1;
endif;

Remember, if a variable is equal to zero then as a boolean it is considered as false, it is also considered as empty (annoying when you have a required form field and someone enters a zero as the value).

So that’s variable casting. It’s a quick and easy way to secure any integer input.

Check for the Expected

Going back to my original example, imagine a form that has a select list. Let’s say it’s a simple contact form and at the bottom it has a list to allow people to select one option to say how they found the site (a waste of time in my opinion but clients still request it!). So your HTML code would look like the following:

<label for="findus">How did you find us?</label>
<select id="findus" name="findus">
<option value="Search Engine">Search Engine</option>
<option value="Another Site">Another Site</option>
<option value="From a Friend">From a Friend</option>
<option value="Other">Other</option>
</select>

In a safe and kind world we would just expect that someone would complete the form and the script that processes it would just need to accept the $_POST[‘findus’] value and perhaps input it into a database table or put it in an email. However, we don’t use a safe and kind internet unfortunately. We live in a world where 15 year old kids want to hack sites, inject rubbish into our database tables, or spammers want to submit their rubbish via every form going, regardless of whether you need a new fake Rolex or various tablets 😉

There are 3 ways to process this data. The naive route, assuming that the only data that the $_POST[‘findus’] variable will contain is one of those 4 options. I see this all the time on sites I take over from others, or people asking for help with their code and you cringe when you read their PHP. Do not assume the form posted is yours!!! Okay I’ve said it now. You cannot assume the form that is posted will contain the HTML that I wrote above. If your action URL is say http://www.yoursite.com/contact-us.php then any form or spam program on the web can post to that. Just because you’ve said that the findus variable is a select list on your form, doesn’t mean that Spammer A or Hacker B is going to treat the variable with the same respect. So you need to secure everything, not just text inputs, especially if you’re going to enter this information into a database table.

The second way to process this data is to run the variable through an escapeString function. This will then secure the variable before entering it into a database table (note, if you’re just emailing the information then this method of securing isn’t really needed).

However, the third and most suitable process is to check that what has been submitted is what has been expected. Spammers will often submit the correct form variables but they will contain completely different values than they should. If you have a select list of 4 items then why not just compare the value received with one of these values to make sure there is a match? This is easily done with an array. First off you store the values in an array:

$findusValues = array("Search Engine", "Another Site", "From a Friend", "Other");

Then in your processing script you can use the in_array() to check to see if the value of the posted variable exists in the findusValues array.

if (!in_array($_POST['findus'], $findusValues)) :
//value isn't in the array so create error message here
endif;

If the value of $_POST[‘findus’] contains anything other than what is in the array then the if statement will be false and your error processing should take over. Of course it would be silly to leave the HTML for the select list as above as a simple typing error could cause problems for people. Plus why repeat something when a few lines of code will take of it?

<label for="findus">How did you find us?</label>
<select id="findus" name="findus">
<?php
foreach ($findusValues AS $value) :
echo "<option value='".$value."'>".$value."\n";
endforeach;
?>
</select>

Of course this means that the array $findusValues needs to be defined in the page that contains the form. I general tend to have a form submit to itself and the processing goes on above the HTML doctype. This way if there is a problem it’s easy enough to redisplay the values in the form and allow the user to correct their mistakes. So the order of the page would be

  • Define Array
  • Form processing script
  • HTML output including form

Summary

I’ll admit that I’ve been naive in the past. When I first learnt PHP I didn’t realise such things as SQL injections existed. The book I learnt from never told me that. Then when I was introduced to the addslashes() function I was the same as most newbie PHP developers and assumed that radio buttons, checkboxes and select lists, all values that were predefined, would be fine and not need checking. As a newbie PHPer I certainly wish someone had told me these things earlier on.

You may also like...

4 Responses

  1. Andrew says:

    That’s good advice Sarah.

    My learning process was similar, the book I used didn’t have much to say about validation either.

  2. Sarah says:

    I don’t think the beginner books tend to which is a shame as a short chapter on basic security is a must really.

    As a side note, I did forget to mention that anyone interested in security should also look at the Php|architect’s Guide to PHP Security.

  3. ses5909 says:

    It’s definitely very important. I typically use the “mysql_real_escape_string” function and there is a similar function for postgres as well.

  4. Sarah says:

    That’s what my escapeString function uses. Checks if magic quotes are on, if so undoes their work then uses the mysql_real_escape_string to secure the input.

    But using the array method whenever possible covers both security and potentially cuts down on spam too.

Leave a Reply

Your email address will not be published. Required fields are marked *

%d bloggers like this: