ONLamp.com    
 Published on ONLamp.com (http://www.onlamp.com/)
 See this if you're having trouble printing code examples


Ten Security Checks for PHP, Part 1

by Clancy Malcolm
03/20/2003

Web applications have become a popular way to provide global access to data, services, and products. While this global access is one of the Web's underlying advantages, any security holes in these applications are also globally exposed and frequently exploited. It is extremely easy to write applications that contain unintentional security holes. This is demonstrated by the range of common web applications, including PHPMyAdmin, PHPShop and FreeTrade, that have contained major security holes. The source code is often required to identify these holes, but it is common to make the source code of these applications available to the public. This article provides five steps to help identify or avoid such security holes in applications written using PHP.

Avoid Using Variables When Accessing Files

Consider the following code:

// $lib_dir is an optional configuration variable
include($lib_dir . "functions.inc");

or worse still:

// $page is a variable from the URL
include($page); 

The user could set the $lib_dir or $page variables and include files such as /etc/passwd or remote files such as http://www.some-site.com/whatever.php with malicious code. This malicious code could potentially delete files, corrupt databases, or change the values of variables used to track authentication status.

What to Look For

Search code for the following functions:

Possible Fixes or Improvements

Escape characters in SQL statements

A common mistake is to use a variable value supplied by the user or the URL in an SQL query without escaping special characters. Consider the following fragment of code from a script designed to authenticate a username and password entered in a HTML form:

$query = "SELECT * FROM users WHERE username='" . $username . "' 
          AND password='" . $password . "'";

// the record exists function is defined elsewhere
if (record_exists($query)) {
	echo "Access granted";
} else {
	echo "Access denied";
}

his code would work when accessed using check.php?username=admin&password=x. However, if the code were accessed using check.php?username=admin&password=a%27+OR+1%3Di%271 (and if magic_quotes_gpc were disabled) then the password condition becomes Password='a' or 1='1' so that the admin user record would always be returned regardless of the password it contained.

This problem is partly avoided when the magic_quotes_gpc variable is on in the php.ini file, meaning that PHP will escape quotes in GET, POST, and cookie data using the \ character. However, magic_quotes_gpc is frequently disabled because it could make other code behave strangely. Given a line containing echo $username in the above code fragment, any occurrences of ' would be replaced by \'). Furthermore the magic_quotes_gpc variable does not protect against variable values obtained from sources such as database records or files which a malicious user may have already modified during normal program operation.

Related Reading

Web Database Applications with PHP, and MySQL
By Hugh E. Williams, David Lane

What to Look For

Search for the query functions for your database. For example, if you are using MySQL, search for usage of the mysql_db_query function.

Possible Fixes or Improvements

Do Not Trust Global Variables

If the register_globals option is enabled, PHP will create global variables for each GET, POST, and cookie variable included in the HTTP request. This means that a malicious user may be able to set variables unexpectedly. Consider the following code aimed to allow anonymous access to a single article and require authentication for all other articles:

// Assume $article_id is set by the URL
if ($article_id == 0) {
	$guest_ok = true;
}

if (!$guest_ok) {
	// Check user is authenticated using a function defined elsewhere
	check_auth();
} 

This code may appear to work, because the $guest_ok variable will generally be initialized to false. However, if a malicious user includes guest_ok=1 in the URL, he will be granted access to any article in the system.

A similar problem can arise when you perform security checks when showing links to pages but do not perform security checks on the linked pages themselves. In a system where users are granted access to a select list of articles, you should perform security checks when producing the list of available articles and when displaying an article selected from the list. Without this checking, a malicious user could type URLs for articles to which he should not have access and view the article successfully. Another common variation of this problem is to implement a "Remember My Login" feature by storing a user identifier in a cookie, allowing users to change their cookie value to login as whomever they want.

What to Look For

This problem can appear almost anywhere in your code. Pay careful attention to the following areas:

Possible Fixes or Improvements

Avoid False Uploads

File uploads can suffer from a severe case of the untrusted global variables problem that is worth considering as an additional problem. When a file is uploaded, a PHP script is given a variable that provides the name of the temporary file where PHP saves the uploaded file. However, the user could construct a URL that sets this variable to a malicious value such as /etc/passwd and not upload a file. The responding script may then copy that file to an accessible location or display the file's contents to the user.

What to Look For

Examine all scripts that respond to file uploads. Searching for type="file" may help identify these scripts.

Possible Fixes or Improvements

Escape HTML Characters in Text

What happens if somebody puts a <blink> tag in a posting to a discussion board? If you don't escape HTML characters in text either before you save or display it, all subsequent text on the page could be blinking. More severe versions of this attack are also possible; for example an attacker could write JavaScript that takes the browser to a competitor's site.

Related Reading

Web Security, Privacy & Commerce
By Simson Garfinkel

What to Look For

Identify pages which display text entered by untrusted users.

Possible Fixes or Improvements

Further Ideas

Part two of this article will feature five more security checks for PHP. In the meantime, here are three ideas to keep in mind as you design your application.

Clancy Malcolm is a private web application consultant and contributes to numerous open source projects.


Return to the PHP DevCenter.

Copyright © 2009 O'Reilly Media, Inc.