Securing PHP Web Applications

Security

It’s not a guarantee of security—there will always be new vulnerabilities and new exploits—but at least you’ll know that if someone is going to hack your application, he or she is going to have to work at it.
Infrastructure Functions

As you design your application, you’ll find that there are certain functions you’re going to need more than once—database insert and retrieval, for instance. These are the things you want to write first, because they are the foundation of your application. Once they’re done, you can forget about them. Here are the infrastructure functions we wrote for the guestbook application:

    getDatabaseHandle(): Handles connecting to the database. Returns a database handle.

    getDisplayComments($numComments): Retrieves the most recent comments from the database. Takes the optional parameter $numComments that governs how many comments to retrieve. This defaults to ten. Returns an associative array keyed on datestamp.

    storeComment($comment, $image, $username): Stores comments in the database. Inserts the comment, image, and username (if available) directly in the Comments table.

    User::new($username, $password, $email): Constructor for the User object. Returns a reference to the instantiated object. Does not store data in the database. Call the update() function to store user data.

    User::load($username): Retrieves user data from the database and uses the constructor to instantiate a User object. Returns a reference to the instantiated object or NULL on failure.

    User->update(): Inserts or updates the database with the data stored in the object. Returns a Boolean—TRUE on success, FALSE on failure.

    User->isAdmin(): Returns TRUE if the user is an administrative user, FALSE otherwise.

    User->makeAdmin(): Stores the value Y in the local $user->isAdmin variable. Calls User->update() to store the information in the database. Returns TRUE on success. Calls errorHandler() on failure and returns FALSE.

    errorHandler($message, $user): Logs errors to the log file and to the local $user->errormsg variable (if available). Returns $message formatted for output to the browser or $user object, if available.

    Login($username, $password): Authenticates the user and instantiates a User object.

    Logout ($username): Invalidates the session ID associated with the username and redirects the browser to the public side of the Web site.

Choose Solid Test Data

Remember to check odd situations, boundary conditions, and other special cases.

Boundary conditions are the most extreme cases you can think of. When we test the constructor, which takes a username as data, the following may be useful boundary conditions:

  •     NULL data
  •     Length exceeding the size of the variable
  •     Data including ASCII control characters
  •     Data including special characters, such as & and *
  •     Data that replicates an injection attack, such as ;drop table users;
  •     Any other extreme data you can think of

These are tests that you expect to fail—in fact, if they don’t fail, you know that you need to go back and harden your code some more.

function authenticateUser($tainted_username, $tainted_password) {
      // Set up our variables
      $username = NULL;
      $password = NULL;
      if (validateUsernamePassword($tainted_username, $tainted_password)) {
            // At this point we can safely assume that both $username and $password
            // are legitimate
            $username = $tainted_username;
            $password = $tainted_password;
      }
      // The login() function will return either a user object (if the username and
      // password are found in the database) or FALSE. If $username and
      // $password are false at this point, they won't be found in the database, so
      // login() will return FALSE.
      return login($username, $password);
}
function validateUsernamePassword($tainted_username, $tainted_password) {
      // Set up our variables
      if (strlen($tainted_filename) > 256 || (strlen($tainted_password) > 256 &&
      strlen($tainted_password) < 8)) {
            //return FALSE; //Bail
      }

      $username = NULL; // This will hold the validated username
      $password = NULL; // This will hold the validated password

      // Validate username
      if(preg_match("/^[A-Za-z0-9]*$/", $tainted_username)) {
            $username = $tainted_username;
            if(preg_match("/^[A-Za-z0-9@*#_]{8,}$/"), $tainted_password) {
                  $password = $tainted_password;
            } else {
                  return FALSE; //Bail
            }
      } else {
            return FALSE; // Bail
      }
      return TRUE;
}

Exploit Testing Tools

Bibliographical Information

Securing PHP Web Applications

By: Tricia Ballad; William Ballad
Publisher: Addison-Wesley Professional
Pub. Date: December 16, 2008
Print ISBN-10: 0-321-53434-4
Print ISBN-13: 978-0-321-53434-7