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.

+

PHP is a very popular language. 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 [http://www.sans.org/top20/#c3 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.

This article is the underlying research behind the [http://www.sans.org/top20/#c3 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.

Line 9:

Line 9:

===About "safe_mode"===

===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.

+

Safe Mode is a set of controls, which when turned on:

+

+

* forces PHP to test for UID permission before opening files. You can relax this to GID by enabling safe_mode_gid, but this usually implies access as if safe mode was disabled)

+

* prevents system() and other calls from working (unless safe_mode_exec_dir is set)

* open_basedir allows hosters to force file access to stay within a virtual directory, but otherwise it is not set

+

+

However, there are many ways around safe_mode. Some legitimate programs contain these methods. Almost all PHP exploits use such methods to get around safe_mode restrictions.

'''''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.

'''''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.

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.

+

+

Safe Mode has been removed in PHP 6.0.

===addslashes() / magic_quotes===

===addslashes() / magic_quotes===

Line 22:

Line 31:

* magic_quotes_gpc is disabled in all PHP installations

* magic_quotes_gpc is disabled in all PHP installations

* addslashes() should be deprecated - it does not protect against SQL injections

* 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

+

* Software should move post-haste to support safe(r) database access mechanisms, such as PDO or mysql_real_escape_string()

−

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.

+

PDO 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. Hosters should move to the latest PHP as quickly as possible to encourage the adoption of safer software.

(Code from XMB 1.8, where $db->query() is essentially a wrapped mysql_query($conn, $sql))

+

+

Such code is vulnerable to attack due to the lack of escaping.

−

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.

+

Stefan Esser [http://blog.php-security.org/archives/35-Teaching-the-Teachers.html points out] that using user-supplied data for the table name (in the above example, $table_members, is for all intents and purposes unsecurable, and should not be used, as mysql_escape_realstring() or $mysqli->escape_string() and other escaping mechanisms do not expect to be dealing with data prior to the WHERE clause, only WHERE, ORDER BY, etc data enclosed by quotes. Therefore, we do not recommend the use of dynamic table names if it can be avoided.

===How to Protect Against It===

===How to Protect Against It===

Line 282:

Line 312:

'''''Developers''''' should:

'''''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:

+

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

+

+

Using mysql_real_escape_string()

+

+

UltimaBB, XMB's descendant, wraps mysql_real_escape_string() in $db->escape() as it has a data factory which will eventually support other databases than MySQL. formVar() forces the data to come from the $_POST array, which reduces the attack surface area.

If this code base was converted from its own data layer to PDO, this is what it might look like:

−

* 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.

+

<pre>

<pre>

−

$sql = "SELECT * FROM “.

+

$regusername = $db->escape(formVar('regusername'));

−

mysql_real_escape_string($_POST['DBHOST_TBL_PREFIX']).

+

$regemail = $db->escape(formVar('regemail'));

−

”themes WHERE themeid='”.intval($themeid)."'";

+

−

mysql_query($sql, $conn);

+

if (empty($regusername))

+

{

+

cp_error($lang['regempty'], false, '', '</td></tr></table>');

+

}

+

+

if (preg_match("/[\]\['".'",!@#~$%\^&*()+=\/\\\\:;?|.<>{}]/', $regusername))

* As a last resort, code should be using mysql_real_escape_string() (but not addslashes() which is insufficient). This provides limited protection to simple SQL injections, but is the absolute minimum required for all applications trying to use the native database interfaces.

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

* 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.

'''''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.

+

'''''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.

* Try to move secrets and logs out of the web root if at all possible – see the references on “Shared Hosting” by Chris Shiflett

* 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

* Ensure that your scripts are compatible with safe mode restrictions and will work under suPHP or other user execution wrapper

+

* Session tampering can be hindered by setting your scripts to use a non-default session dir. Use [http://no2.php.net/session_save_path session_save_path()] or set [http://no2.php.net/manual/en/ref.session.php#ini.session.save-path session_save_path] in a .htaccess file

'''''Hosters'''''

'''''Hosters'''''

Line 430:

Line 523:

* 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)

* 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)

* 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)

Stefan Esser provided [http://blog.php-security.org/archives/35-Teaching-the-Teachers.html feedback via his blog], which has been incorporated 12 July 2006.

+

+

OWASP welcomes peer review and constructive criticism for all its materials. If you wish to provide feedback, please e-mail the author [[User:Vanderaj|Andrew van der Stock]].

[[User:Vanderaj|Vanderaj]] 00:15, 25 June 2006 (EDT)

[[User:Vanderaj|Vanderaj]] 00:15, 25 June 2006 (EDT)

[[Category:PHP]]

[[Category:PHP]]

[[Category:OWASP PHP Project]]

[[Category:OWASP PHP Project]]

Latest revision as of 14:50, 16 January 2007

PHP is a very popular language. 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.

Special notes for this edition

About "safe_mode"

forces PHP to test for UID permission before opening files. You can relax this to GID by enabling safe_mode_gid, but this usually implies access as if safe mode was disabled)

prevents system() and other calls from working (unless safe_mode_exec_dir is set)

restrictions on setting most environment variables (but not reading them)

open_basedir allows hosters to force file access to stay within a virtual directory, but otherwise it is not set

However, there are many ways around safe_mode. Some legitimate programs contain these methods. Almost all PHP exploits use such methods to get around safe_mode restrictions.

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.

Safe Mode has been removed in PHP 6.0.

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 or mysql_real_escape_string()

PDO 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. Hosters should move to the latest PHP as quickly as possible to encourage the adoption of safer software.

P1: Remote Code Execution

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:

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.

P2: Cross-site scripting

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:

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:

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:

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.

P3: SQL Injection

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:

(Code from XMB 1.8, where $db->query() is essentially a wrapped mysql_query($conn, $sql))

Such code is vulnerable to attack due to the lack of escaping.

Stefan Esser points out that using user-supplied data for the table name (in the above example, $table_members, is for all intents and purposes unsecurable, and should not be used, as mysql_escape_realstring() or $mysqli->escape_string() and other escaping mechanisms do not expect to be dealing with data prior to the WHERE clause, only WHERE, ORDER BY, etc data enclosed by quotes. Therefore, we do not recommend the use of dynamic table names if it can be avoided.

How to Protect Against It

Developers should:

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

Using mysql_real_escape_string()

UltimaBB, XMB's descendant, wraps mysql_real_escape_string() in $db->escape() as it has a data factory which will eventually support other databases than MySQL. formVar() forces the data to come from the $_POST array, which reduces the attack surface area.

As a last resort, code should be using mysql_real_escape_string() (but not addslashes() which is insufficient). This provides limited protection to simple SQL injections, but is the absolute minimum required for all applications trying to use the native database interfaces.

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.

P4: PHP Configuration

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.