Validating form input

If you're just getting started with PHP, security may not seem like a very important thing to focus on right now. Unfortunately, this is the reason for PHP having a bad reputation when talking about security for a long time now. When it's so easy to get started, a lot of people write a lot of code without ever worrying about the security. In this chapter, we will have a look at some of the security pitfalls when working with form data.

There is one rule that you should always remember when working with data from the outside world: Never trust it! If you have a form where the user can submit various data, don't assume anything about it. Validate it before you use it! The most common security problem is the so-called SQL injections, where a malicious visitor injects SQL into your database queries, allowing him to e.g. delete your tables. The most common way of doing this is either through the query string (the page address) or through a form.

In a later chapter, I will show you why SQL injections can be so dangerous, but for now, we will look at another danger when trusting the user data too much. Consider the following example:
<?php
if(isset($_POST["selRating"]))
{
    $number = $_POST["selRating"];
    echo "Selected rating: " . $number;
    // Write the rating to the database here
}
?>
<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post">
    Select a rating from 1 to 5:
    <select name="selRating">
        <option value="1">1</option>
        <option value="2">2</option>
        <option value="3">3</option>
        <option value="4">4</option>
        <option value="5">5</option>
    </select>
    <input type="submit" name="btnSendForm" value="Send" />
</form>
This is a minimal version of a rating system. You have probably seen them at lots of sites - a simple dropdown list lets the user choose a rating, usually from 1 to 5, and then submit the form. Behind the scenes, the rating is added to a database, and when the total rating is to be displayed, the script sums up all the ratings and then divides with the number of ratings. It's all very simple and neat and if you run this example, you might think that this is totally safe, since the user has to select a value from the dropdown list. We use the POST method, so the user can't just enter another value in the query string. So, this is safe, right?

No, not at all. Sending POST data to a page is only a tiny bit harder than manipulating the query string of a GET request. If you have saved the above example to a file, try creating a new file, and pasting this example:
<form action="otherfile.php" method="post">
    Fake rating: <input type="text" name="selRating" value="1000" />
    <input type="submit" name="btnSendForm" value="Send" />
</form>
Change the action value from otherfile.php to the name you gave the file from the first example and open this one in a webbrowser. The form is a bit different, but it uses the exact same field names. If you hit the Send button, the value will be posted to otherfile.php and treated as if it came from the dropdown list - no questions asked! If you throw in a rating of 1000 instead of 5 into the database, your ratings will get completely wrong. This is of course a small problem compared to what can be done when not validating the input. For instance, the user may submit an SQL string instead of a number, and once you try to input what you think is a number to your database, you may actually be running the malicious persons SQL code instead.

In this case, it's very easy to validate the data though. Change the PHP code in the first example to something like this:

<?php
if(isset($_POST["selRating"]))
{    
    $number = $_POST["selRating"];
    if((is_numeric($number)) && ($number > 0) && ($number < 6))
    {
        echo "Selected rating: " . $number;
        // Write the rating to the database here
    }
    else
        echo "The rating has to be a number between 1 and 5!";
}
?>
We have added 3 simple checks: First of all, it has to be a number, which we check with the is_numeric() function. It simply tells us if the parameter we pass is a number or at least a string that can be converted directly to a number. After that, we require that $number has to be bigger than 0, and smaller than 6, which leaves us with 1-5 as the only possible values. We have just secured the form. Every time you use a piece of data which comes from the user, you should think about how to validate it properly before using it.
<Previous
^ Back to Top