PHP Top 5

PHP is a very popular language with many flawed security "features". Every PHP developer and hoster should understand the primary attack vectors being used by attackers against PHP applications.

This article is the underlying research behind the SANS Top 20 2005's PHP section. The methodology used in the preparation of this article is to review all Bugtraq postings containing the word "PHP" and categorize each unique flaw. The author analyzed the most popular flaws / attacks, and researched prevention techniques, resulting in this article.

If you need more information on how to write solid, secure PHP, please consult the references.

About "safe_mode"
Safe Mode implemented without design or thought is simply not safe. It cannot be made to be safe, and does not add any actual safety. Most of the restrictions implemented by safe mode can be worked around by clever hacks. These have been extensively used by software which needs to get around the safe_mode restrictions to operate, and many PHP attacks which seem to bypass it like a hot knife running through butter.

Hosters, do not just switch safe_mode on and lock it down hard. Such controls rarely work as expected, and more to the point it does not prevent any of the five major attack vectors presented in this paper. Only code reviews and ensuring that code is tested for security flaws can the risk of attack be reduced.

This is not to say that safe_mode is useless. Well designed software can use thoughtfully chosen safe_mode restrictions to improve defense in depth. Such software should be given that opportunity by using .htaccess (or similar mechanisms) to selectively enable safe_mode restrictions as they need.

addslashes / magic_quotes
An earlier version of this article recommended the use of addslashes. In general, this is poor advice today, particularly when PHP is coupled with the most popular open source database, MySQL.

To prevent SQL injections, it is essential that:


 * magic_quotes_gpc is disabled in all PHP installations
 * addslashes should be deprecated - it does not protect against SQL injections
 * Software should move post-haste to support safe(r) database access mechanisms, such as PDO

The latter requires hosters to upgrade their PHP to the latest PHP 5.1. Unfortunately, there has been low take up of the safer versions of PHP due to perceived incompatibilities with much of the PHP software base. Software which is incompatible with PHP 5.x is simply insecure, and should not be run. It is our opinion that PHP 4 is unsecurable as it does not possess PDO and a number of other safer interfaces. PHP 4.x should not be run in any shared or dedicated environment of any consequence. Hosters should give notice to their clients that they have only a short time to bring their software up to date with PHP 5.x and rapidly adopt the latest version of PHP.

Description
The most widespread PHP security issue since July 2004 is remote code execution, mostly via file system calls.

The root causes of this issue are:


 * Insufficient validation of user input prior to dynamic file system calls, such as require or include or fopen
 * allow_url_fopen and PHP wrappers allow this behavior by default, which is unnecessary for most applications
 * Poor permissions and planning by many hosters allowing excessive default privileges and wide ranging access to what should be off limits areas.

From PHP 4.0.4 onwards, allow_url_fopen was enabled by default, making poorly written applications vulnerable through no changes of their own. As of PHP 4.3.4, the PHP project changed the access on this feature to PHP_INI_SYSTEM, which prevents code from easily disabling this feature via the use of ini_set. This has the unfortunate effect that only hosters can change this setting, many of whom cannot make wholesale changes to php.ini without breaking their customers code, including well-written code which relies upon these features.

Operating Systems Affected

 * PHP 4 (after PHP 4.0.4), 5.x

CVE/CAN Entries
There are more than 100 such vulnerabilities reported since July 30, 2004. These are a representative sample:

phpBB Remote Code Execution Vulnerability http://www.securityfocus.com/bid/14086

TikiWiki Remote Code Execution Vulnerabilities http://www.securityfocus.com/bid/12328

XML-RPC Remote Code Execution (many vendors) http://www.securityfocus.com/bid/14088

How to Determine if you are Vulnerable
Inspect your code for constructs like:

$report = $_POST[‘report_name’]; include $report;

or

$username = $_POST[‘username’]; eval(“echo $username”);

The above snippets are not exhaustive. Other code constructs to look for include:


 * fopen, fsockopen
 * Direct command execution - popen, system, ` (backtick operator). Allows remote attackers to execute code on the system without necessarily introducing remote code.
 * Direct PHP code execution via eval
 * Limited evaluation if the attacker supplied PHP code is then used within double quotes in the application code – most useful as an information disclosure
 * include, include_once, require, require_once with dynamic inputs
 * file_get_contents
 * imagecreatefromXXX
 * mkdir, unlink and rmdir and so on - PHP 5.0 and later has limited support for some URL wrappers for almost all file functions

How to Protect Against It
Developers should


 * Review existing code for file operations, include/require, and eval statements to ensure that user input is properly validated prior to first use
 * When writing new code, try to limit the use of dynamic inputs from users to vulnerable functions either directly or via wrappers

Hosters should:


 * Disable allow_url_fopen in php.ini by setting it to 0
 * Enable safe_mode and set open_basedir restrictions (if you know what you're doing - it's not really that safe!)
 * Lockdown the server environment to prevent the server from making new outbound requests

OWASP calls on the PHP Project to by default disable remote file support and associated wrappers, and allow applications that require these features to selectively enable them on a per application basis.

Description
Cross-site scripting (also known as HTML injection or user agent injection) with PHP is possible in all three modes: reflected, persistent and DOM injection.
 * Reflected	The attacker provides a link or other payload containing embedded malicious content, which the application immediately displays back to the victim. This is the primary form of phishing via e-mail (such as eBay scams, bank scams, etc)


 * Persistent	The attacker stores malicious content within a database, which is then exposed to victims at a later time. This is the most common form of cross-site scripting attack against forum and web mail software.


 * DOM 	The attacker uses the victim site’s JavaScript code to perform reflected cross-site scripting. This technique is not widely used as yet, but it is just as devastating as any form of cross-site scripting.

PHP has no inbuilt mechanism to protect against cross-site scripting in an automated fashion, therefore unless developers remediate this issue, an application will be vulnerable by default.

Operating Systems Affected
All versions of PHP

CVE/CAN Entries
There are more than 100 XSS CVE / CAN entries since July 2004. The following are representative samples:

VBulletin Cross-site scripting http://www.securityfocus.com/bid/14874

Coppermine Display Image Cross-site scripting http://www.securityfocus.com/bid/14625

WordPress Edit Cross-site Scripting http://www.securityfocus.com/bid/13664

How to Determine if you are Vulnerable
Developers


 * Does the application rely upon register_globals to work? If so, your application is at a slightly higher risk, particularly if you do not validate input correctly.


 * Inspect user input handling code for unsafe inputs:

echo $_POST[‘input’];

At the very least, the code should do this:

echo htmlentities($_POST[‘input’], ENT_QUOTES, ‘UTF-8’);

The best code will inspect user input for type, length, and syntax. Chris Shiflett recommends validation code like this:

$html = array;

$html['username'] = htmlentities($clean['username'], ENT_QUOTES, 'UTF-8');

echo " Welcome back, {$html['username']}. ";

For more details on Shiflett’s approach, please refer to the references below. As every application has different input fields, is up to every application to do this properly – there is no automatic way of doing this task.

If you rely upon stored data that may potentially be tainted, your output code should protect users regardless of if you currently filter new input for HTML entities:

echo htmlentities($output);

Code that performs basic black listing is particularly prone to attack:

$input = str_replace(“<”, “&lt;”, $input); $input = str_replace(“>”, “&gt;”, $input); $input = str_replace(“script”, “”, $input);

Such code should be refactored to use HTML entities and white listing approaches, for example, the simpler and safer:

$input = htmlentities($input, ENT_QUOTES, ‘UTF-8’);

If you use Javascript to redirect the user (via document.location or window.open any similar means), output to the user via document.write, or modifies the DOM in any way, you are likely to be at risk of DOM injection.

How to Protect Against It
Developers should:


 * Turn off register_globals and ensure all variables are properly initialized
 * Obtain user input directly from the correct location ($_POST, $_GET, etc) rather than relying on register_globals or the request object ($_REQUEST)
 * Validate input properly for type, length, and syntax
 * Free text input can only be safely re-displayed to the user after using HTML entities
 * Variables sent back to the user via URLs must be URL encoded using urlencode, although the use of GET requests is deprecated for anything besides navigation purposes:

$html = array; $url = array;

$url['username'] = urlencode($clean['username']);

$link = "http://example.org/index.php?username={$url['username']}";

$html['link'] = htmlentities($link, ENT_QUOTES, 'UTF-8');

echo "Link";


 * Validate JavaScript code against Klein’s DOM Injection paper (see references) to ensure that they are immune from DOM injection attacks

At this time, OWASP recommends all applications move to directly accessing only those variables they require from the correct user input array ($_POST, $_GET, $_COOKIE, etc) rather than rely upon the get, post, cookie (GPC) behavior of register globals or $_REQUEST. It is strongly recommended you do not use $_REQUEST.

Hosters are unable to configure PHP to be cross-site scripting safe, so should consider removing applications that have a history of being vulnerable to XSS attacks. This will force developers to improve their XSS protections.

Description
SQL injection is one of the oldest attacks against web applications. However, as it is well known, there are many techniques to defend against it:


 * Validating data prior to using it within dynamic SQL queries
 * Always prefer positive validation rather than black listing
 * Using PDO (available via PECL for PHP 5.0, and is included in PHP 5.1 and later)
 * Using MySQLi’s or PEAR::DB’s parameterized statements
 * At the very least, use functions like mysql_real_escape_string. All PHP database interfaces have basic SQL escaping functions

Unfortunately, with PHP 4.x, it is up to the PHP coder to write robust code that is safe against SQL injections. At this time, PHP programs should be migrating to PHP 5.1 and using PDO, which has a safe SQL interface which avoids all of these issues.

Using addslashes is simply insufficient. As Chris Shiflett has demonstrated, addslashes is simply insufficient to protect against SQL injection. It is the OWASP Project's view that addslashes must be deprecated in favor of safe interfaces.

There is some dissent in the PHP non-security community about the default behavior of PHP’s magic quotes, which “magically” adds slashes (not quotes) to input data on the basis that it might be destined for a database. As this behavior is not always available, code must be written with the assumption it will not be configured. OWASP’s issue with magic quotes is that it gives a false sense of security – it is not a silver bullet and it is insufficient to protect against advanced SQL injection techniques.

Operating Systems Affected
All versions of PHP

CVE/CAN Entries
As there are more than 100 CVE / CAN entries from multiple vendors (in fact, Bugtraq usually offers up two to three different PHP applications with SQL injection vulnerabilities per day), this is a representative sample only:

PHP Nuke SQL Injection http://www.securityfocus.com/bid/9544

OS Commerce SQL Injection http://www.securityfocus.com/bid/15023

VBulletin SQL Injection http://www.securityfocus.com/bid/14872

How to Determine if you are Vulnerable
Find code which calls mysql_query or similar database interfaces Inspect if any calls create dynamic queries using user input:

dbquery("DELETE FROM ".$_POST['DBHOST_TBL_PREFIX']."themes"); (Example from FUDForum2 installer)

Such code is vulnerable. As the above example shows, most applications use a db wrapper class, like PEAR::DB or a custom thin layer, so looking for just mysql_ may not be sufficient.

How to Protect Against It
Developers should:


 * Migrate their code to PHP 5.1 and use PDO, or if this is not possible, at least migrate their code to safer constructs, such as PEAR::DB’s parameterized statements or the MySQLi interfaces:

$sql = "SELECT * FROM “. $mysqli->escape_string($_POST['DBHOST_TBL_PREFIX']). ”themes WHERE themeid=?”;

$statement = $mysqli->prepare($sql); $statement->bind_param(’i’, $themeid); $statement->execute;


 * Validate data for correct type, length, and syntax. Always prefer white listing (positive validation) data over black listing, which is akin to virus patterns – always out of date, and always insufficient against advanced attacks
 * In the absolute worst case scenario – PHP 4.x and PEAR is unavailable, code should be using mysql_real_escape_string or similar escaping mechanisms. This provides limited protection to simple SQL injections, but is the absolute minimum required for all applications trying to use the native database interfaces.

$sql = "SELECT * FROM “. mysql_real_escape_string($_POST['DBHOST_TBL_PREFIX']). ”themes WHERE themeid='”.intval($themeid)."'";

mysql_query($sql, $conn);


 * Provide a .htaccess file to ensure that register_globals and magic_quotes are forced off, and that all variables are properly initialized and validated

Developers should carefully test their applications against SQL injection prior to release, particularly those SQL statements that consume user data. A good tool for this is WebScarab that tests for basic SQL injections in a relatively automated fashion.

Hosters cannot prevent SQL injection via technical means, but they can reduce their exposure by using PHP 5.1, and providing PDO to their clients. They should carefully configure MySQL or their favorite database in the most secure fashion possible. Users should not be granted administrative privileges over their databases if at all possible, and the database should be running in a chrooted environment to minimize damage from any successful attacks.

Description
PHP Configuration has a direct bearing on the severity of attacks. Although no particular CVE entries are found against configuration, poor configuration choices maximize the attacker’s advantage and damage they can cause poorly configured systems. What is worse, many “security” options in PHP are set incorrectly by default and give a false sense of security.

It is surprising that there is no agreed "secure" PHP configuration, and even more surprising that this is not how PHP is configured by default. There are arguments for and against the most common security options:


 * register_globals (off by default in modern PHP, should be off)
 * allow_url_fopen (enabled by default, should be off)
 * magic_quotes_gpc (on by default in modern PHP, should be off)
 * magic_quotes_runtime (off by default in modern PHP, should be off)
 * safe_mode and open_basedir (disabled by default, should be enabled and correctly configured. Be aware that safe_mode really isn't safe and can be worse than useless)

Due to disagreement on the best settings, and the PHP Project’s preference to enable features over security, all but a very few PHP installations are properly secured.

OWASP strongly recommends the PHP Project coordinate with acknowledged PHP security professionals and the web hosting community to come up with a secure default configuration, even at the expense of backward compatibility.

Operating Systems Affected
All versions of PHP

CVE/CAN Entries
As this relates to post deployment, there are few CVE / CAN references, however, the severity of attacks is far worse than a properly secured PHP solution.

For example, if remote code execution were blocked by the simple expedient of it being disabled, a vulnerable system would not be compromised. If “helpers” such as register_globals and magic_quotes_gpc were always disabled, developers would have to practice good hygiene to simply obtain their data.

How to Determine if you are Vulnerable
Unless the PHP installation has been hardened by a security professional, it is highly likely that all configurations are sub-optimal.

How to Protect Against It
Developers


 * Upgrade applications to use PHP 5. PHP 5’s slightly stricter conformance requirements help eliminate many latent PHP security issues


 * Configure a .htaccess file – most PHP hosting is via Apache, and setting appropriate PHP variables to suit you reduces the guess work. In particular, disable register globals and magic_quotes_gpc


 * During installation, test using ini_get for common hosting mistakes, such as allowing register_globals and warn the user that the hoster has sub-standard security

Hosters


 * Hosters should be upgrading to PHP 5 now and assisting their clients with the move

PHP Project


 * PHP Project to coordinate with professional security organizations such as SANS or OWASP, acknowledged PHP security experts, and web hosting companies to produce a “safe” out of the box configuration


 * PHP Project to always allow applications to disable risky functionality they do not need that may be available (either by mis-configuration or by design).

Description
PHP developers have many ways to obviate security on shared hosts with local file system attacks, particularly in shared environments:


 * Local file inclusion (such as /etc/passwd, configuration files, or logs)
 * Local session tampering (which is usually in /tmp)
 * Local file upload injection (usually part of image attachment handling)

As most hosters run PHP as “nobody” under Apache, local file system vulnerabilities affect all users within a single host.

Operating Systems Affected
PHP 3, 4, 5

CVE/CAN Entries
As there have been many examples over the last year, the following are representative examples only:

MyPHPAdmin Local File Inclusion http://www.securityfocus.com/bid/15053

MyPHPFAQ Logs Unauthorized Access Vulnerability http://www.securityfocus.com/bid/14930

MAXdev MD-Pro Arbitrary Remote File Upload Vulnerability http://www.securityfocus.com/bid/14750

How to Determine if you are Vulnerable
Inspect all file related functionality and determine


 * If user input is being used as part of the filenames
 * If variables involved in the file operations are not initialized prior to first use and register_globals may be available

If so, your application is at risk.

How to Protect Against It
Developers
 * Ensure that all variables are properly initialized prior to first use
 * Ensure that the users can only affect file operations to the degree you had in mind
 * Try to move secrets and logs out of the web root if at all possible – see the references on “Shared Hosting” by Chris Shiflett
 * Ensure that your scripts are compatible with safe mode restrictions and will work under suPHP or other user execution wrapper

Hosters
 * Ensure your environment is hardened (such as using SELinux or similar hardened environments)
 * Move PHP’s session variables from the default location and secure the new location to prevent cross-user session disclosure and tampering (this is not easy)
 * Enable safe_mode as appropriate (there are many options :-) but be aware that safe_mode really isn't safe and can be worse than useless)
 * Use open_base_dir restrictions
 * Ensure that users have a place outside the web root (public_html) to store logs and secrets
 * Run PHP under a least privilege model, preferably as the user, via the use of PHPsuExec, php_suexec or suPHP