Model PHP Script Example: writing relatively safe web forms

This article is obsolete. Please see Use Filter Input Array to Modernize Legacy Code.


I just whipped this script up to demonstrate some techniques for writing relatively safe web forms with PHP. This example doesn't strive to look nice or even be easy to understand. It should be a jumping off point for learning some PDO, some htmlspecialchars, some filter_var, and a functional style of composing pages. The last thing - not such a great idea, but it works for really short pages.

This script is written to try and avoid SQL injection attacks, and cross site scripting (XSS).

Code is attached, and below.

There is a counter-example, of a simple script with many security flaws, linked after the code.

<?php

/* 
 * Sample PHP form and database example.
 * The goal of this sample is to demonstrate reasonable
 * practices for writing safe web forms.
 *
 * It doesn't demonstrate how to write a decent script,
 * but one that's reasonably safe.  A decent script is
 * broken up into more functions, saved out as libraries
 * so the code isn't so "all over the place".
 */

// config variables
$dbhost = 'localhost';
$dbuser = 'mysite';
$dbpass = '12JNdie8Ds3';
$dbname = 'mysite';

/*
 * Test if the form is being requested (GET) or submitted (POST).
 * This really isn't the best way to do this, but it is okay,
 * and it's more explicit than testing for variable values, which
 * is what most examples do.
 */
if ($_SERVER['REQUEST_METHOD']=='GET') {
    view_form();
    exit();
}
// assume it's POST
$x = $_POST['x'];

// x is supposed to be an integer, so we use validation filters to test.
// It returns NULL if it's out of the range of 0 to 6000.
// http://php.net/manual/en/functio...

$errors = array(); // we'll accumulate error messages here

$x = filter_var( $x, FILTER_VALIDATE_INT, 
        array(
            'options'=>array( 
                'default'=>NULL, 
                'min_range'=>0, 
                'max_range'=>6000 
            )
        ) 
     );

// Rather than throw or show errors immediately, we accumulate them.
// Later, they'll be printed out above the form, in red letters.
if ($x==NULL) { 
    $errors[] = "x was not set correctly. It must be an integer from 0 to 6000."; 
}

/* 
 * We do a quick check for errors, and show them if we have them.
 * We'll do this again a little later.
 */
if (count($errors)>0) {
    view_form( $errors );
    exit(); // bail out
}

/* 
 * Connect to the database and save data.
 *
 * This example uses PDO, which takes care of escaping and quoting our
 * data, so we're safer from being hacked with an SQL injection attack.
 */
try {
    $db = new PDO("mysql:host=$dbhost;dbname=$dbname;charset=utf8", $dbuser, $dbpass);
    $stmt = $db->prepare("INSERT INTO table (`x`) VALUES (?)");
    $stmt->execute(array($x));
} catch(PDOException $e) {
    $errors[] = $e->getMessage();
}

/*
 * We check out the $errors array again to see if we had any errors.  If we did,
 * we just display the error message and bail out.  At this point, there's only
 * one possible error, but in a more elaborate script, we may have a few different
 * errors.
 */
if (count($errors)>0) {
    view_form( $errors );
    exit();
} else {
    $data = array();
    $data['x'] = $x;
    view_thankyou( $data );
    exit();
}

// Our library of Views are below.
// These are also known as "picture functions".

function view_form($errors=NULL) {
    echo view_header( 'Example Form' );
    if (count($errors)>0) {
        foreach( $errors as $error ) {
            $printable = htmlspecialchars( $error );
            echo "<p style='color:red'>$printable</p>";
        }
    }
    ?><!-- this is a block of HTML code with embedded PHP -->
        <form method="POST">
            x <input type="text" name="x" />
            <input type="submit" />
        </form>
    <?php
    echo view_footer();
}

function view_thankyou( $data ) {
    echo view_header( 'Thank You' );
    echo "We stored the value ".htmlspecialchars($data['x']);
    echo view_footer();
}

function view_header( $title ) {
    ?>
    <html>
        <head>
            <title><?php echo $title; ?></title>
        </head>
        <body>
    <?php
}

function view_footer() {
    ?>
        </body>
    </html>
    <?php
}
AttachmentSize
example.php_.txt3.48 KB