php security iissues

6
1 PHP Security Issues and Solutions Thomas Böhne May 17, 2003 Abstract More and more people move from static HTML pages to dynamically gener- ated websites. The most widely used language seems to be PHP, followed by ASP (and VB-Script) and Perl. While the dynamic approach offers many advantages, most users seem to over- look the risks that arise from dynamic websites. We show some basic attacks, and some principles to a more secure design. 1 Introduction As for all Internet applications, security is a basic necessity. As for many Internet applications, security is often not enforced properly. In the following sections we show some common errors solutions to the problems. Not all of them will work in all environments; most of them rely on register_globals being enabled, and safe_mode being disabled. 2 Check variables from user space One of the basic advantages in dynamic websites (CGI, to be more precise) is that not only URLs are requested, but that variable assignments can be send as well. The variable assignments are often encoded in URLs delivered by a previous script run, but can be modified easily before resubmitting. Thus, the content of no variable that was sent from a user can be trusted. Especially if the variables should be used as options to function calls like popen or unlink, it is important to check the content very clearly. Consider the following function calls: $fp = popen ("/bin/ls $homedir/public_html/pictures/$_GET[’pictures’]", "r"); echo "Expression $_GET[’expression’] evaluates to:"; echo eval("echo $_GET[’expression’]"); mysql_query("SELECT * FROM names ORDER BY $_GET[’order’] ;", $link); Calling this script with the options pictures=foo ; rm *, expression=unlink("~/.bashrc") or order=foo ; DROP TABLE names would lead to much data being lost (Actu- ally, it would not, since the special characters must to be encoded properly. But most browsers can do that automatically). Many similar examples could be given here; the only solution to the problem is that calls to sensitive functions should either not include user input at all, or only if it has been checked properly (see below). It is sometimes difficult to distinguish sensitive

Upload: aung-khant

Post on 11-Nov-2014

699 views

Category:

Technology


2 download

DESCRIPTION

 

TRANSCRIPT

Page 1: Php Security Iissues

1

PHP Security Issues and Solutions

Thomas Böhne

May 17, 2003

Abstract

More and more people move from static HTML pages to dynamically gener-ated websites. The most widely used language seems to be PHP, followed by ASP(and VB-Script) and Perl.

While the dynamic approach offers many advantages, most users seem to over-look the risks that arise from dynamic websites. We show some basic attacks, andsome principles to a more secure design.

1 Introduction

As for all Internet applications, security is a basic necessity. As for many Internetapplications, security is often not enforced properly.

In the following sections we show some common errors solutions to the problems.Not all of them will work in all environments; most of them rely onregister_globalsbeing enabled, andsafe_mode being disabled.

2 Check variables from user space

One of the basic advantages in dynamic websites (CGI, to be more precise) is thatnot only URLs are requested, but that variable assignments can be send as well. Thevariable assignments are often encoded in URLs delivered by a previous script run, butcan be modified easily before resubmitting. Thus, the content of no variable that wassent from a user can be trusted.

Especially if the variables should be used as options to function calls likepopenor unlink , it is important to check the content very clearly. Consider the followingfunction calls:

$fp = popen ("/bin/ls $homedir/public_html/pictures/$_GET[’pictures’]", "r");echo "Expression $_GET[’expression’] evaluates to:";echo eval("echo $_GET[’expression’]");mysql_query("SELECT * FROM names ORDER BY $_GET[’order’] ;", $link);

Calling this script with the optionspictures=foo ; rm * , expression=unlink("~/.bashrc")or order=foo ; DROP TABLE names would lead to much data being lost (Actu-ally, it would not, since the special characters must to be encoded properly. But mostbrowsers can do that automatically).

Many similar examples could be given here; the only solution to the problem is thatcalls to sensitive functions should either not include user input at all, or only if it hasbeen checked properly (see below). It is sometimes difficult to distinguish sensitive

Page 2: Php Security Iissues

2

functions from not so sensitive functions — in that case, one should always assume theworst and check the input.

The following examples show some ways to make sure that no malicious statementsare executed:

• To check that thepictures variable contains only (more or less valid file-names), one could use a construct like this:

$pic = $_GET[’pictures’];if (! preg_match(’/ˆ[A-Za-z0-9]+$/’, $pic))

$pic = "default";$fp = popen ("/bin/ls $homedir/public_html/pictures/$pic", "r");

After this test, the variable$pic contains only characters and digits. This shouldbe enough to specify a directory, and prevents the execution of other commands.

• A similar construct can be used for the second example:

if (! preg_match(’/ˆ[0-9+-*/ ]+$/’, $expression))

Now $expression should only contain spaces, mathematical operators anddigits.

• For variables that should only take a distinct number of values, we can just checkif it is a valid choice:

$order = $_GET[’order’];if (! in_array($order, array("names", "addresses", "telephone")))

$order = "names";mysql_query("SELECT * FROM names ORDER BY $order ;", $link);

The array contains all valid choices for$order , and if the user submits a valuethat is not in the array, it defaults back to"names" .

• Often, number must be checked to be in a certain range. The following codemakes sure thatmenu is an integer between zero and seven:

$menu = abs((int) $_GET[’menu’]) % 8;

3 Use constants where possible

A constant type should always be used for values that do not change; this paradigmis taught in many programming courses and part of most tutorials. Many current pro-gramming languages provide for constants, but still many programmers ignore themfor whatever reasons. Unfortunately, the declaration of constants in PHP using a func-tion call is not intuitive and requires more typing (keystrokes, not type conversions)than a variable assignment. The following example shows a relatively save (althoughnot perfect) construct:

Page 3: Php Security Iissues

3

# extract from index.phpif ($username == "admin" && $password == "secret")

define("AUTHENTICATED", true);else

define("AUTHENTICATED", false);include("restricted.php");# extract from restricted.phpif (AUTHENTICATED)

doSecretStuff();

Since variables and constants reside in different name-spaces, callingrestricted.php?AUTHENTICATED=truewill not gain access to the secret part (i.e.,$AUTHENTICATEDis different fromAUTHENTICATED).

4 Check file access

In many cases, users split up larger projects into small PHP files. Consider the follow-ing example, where an input variable is properly checked inindex.php but used inshowfile.php :

# extract from index.php:if (! preg_match(’/ˆ[A-Za-z0-9]+$/’, $filename))

$filename = "default";include("showfile.php");# extract from showfile.phpinclude("$filename");

Readers familiar with PHP will recognize quickly that a call toshowfile.php?filename=../../../etc/passwdwould bypass the checking of$filename in index.php (the code inindex.phpmakes sure that$filename consists of only letters and digits, to disable ascending inthe directory tree).

One possible solution to the problem would be checking variables exactly wherethey are needed; however, grouping security checks in a central place seems to bereasonable. In some (but not all) situations the variable can be replaced by a constant(this would eliminate the problem, see previous section). It is also possible to only putthe official starting files (e.g.,index.php ) in a publicly accessible directory on thewebserver, and hide the included files somewhere else. Unfortunately, this is not alwayspossible since some web-hosting services do not offer access to private directories. Athird straightforward solution — that should work on any PHP account — is to definea constant in the starting pages, and require the constant to be set at the beginning ofall included files:

# extract from index.php:define("VALID_ENTRY", true);if (! preg_match(’/ˆ[A-Za-z0-9]+$/’, $filename))

$filename = "default";include("showfile.php");# extract from showfile.phpif (! defined("VALID_ENTRY"))

die("This script cannot be accessed directly.");include("$filename");

Page 4: Php Security Iissues

4

Note that it is crucial to use a constant instead of a variable. The test for$valid_entry == truecould easily be bypassed byshowfile.php?filename=../../../etc/passwd&valid_entry=true .

5 Use safety mechanisms provided by the programminglanguage

Earlier versions of PHP praised the feature that CGI variables (such as GET and POST)were implicitly made global and had the same syntax as normal variables. In fact, thefeature calledregister_globals was very convenient in many places. But soonpeople realized, that it is also a perfect tool to shoot oneself in the foot. Basically,all variables in scripts can be initialized by any user... Finally,register_globalswere disabled by default (somewhen around version 4.0), resulting in a flood of post-ings by PHP users whose scripts did not run properly anymore. However, disablingregister_globals was definitely the correct decision. Still, many installationshave it enabled, and it is the root of many evils. So the first thing that every PHP usershould do, is turn it off (it can be done everywhere; in the PHP configuration file, theApache configuration file, a.htaccess file, or in the code usingini_set ).

Consider the following simple example:

if ( $username == "admin" && $password == "secret")$auth = true

if ($auth == true)doSecretStuff();

Many people would approve this implementation to be relatively secure. Althoughmany elements could be improved, this code would work in many programming lan-guages other than PHP. But ifregister_globals is enabled, requestingfile.php?auth=truewould return the secret content. Disablingregister_globals prevents this kind ofattack.

Another security option that PHP provides issafe_mode . It disables many riskyfunction calls (that are usually not needed) or restricts them in useful ways. However,safe_mode canonly be enabled in the Apache or PHP configuration file. It seems tobe disabled on most web-hosting services.

Note: It is clear that it should be difficult todisablesafe_mode . But wouldn’t it be nice if it could beenabledanywhere? Send me comments!By default, PHP performs basic error handling. However, it seems to be a much

better idea to enable all warnings and notices (error_reporting(E_ALL) ) on thedevelopment system and turn them off (error_reporting(0) ) on the actual websystem (or even better: perform own error handling).

TODO: Write aboutrequire(once) , assert , die/exit/continue , andpreventing the webserver from sending php source code.

6 Expect the worst - deny by default

A basic security concept isdeny everything, allow exceptions, meaning that safer op-tions must always be preferred. I.e., every authentication script should have the form:

Page 5: Php Security Iissues

5

$authenticated = false;if ( your_favorite_access_control_code(options) ) {

$authenticated = true;}if ($authenticated) {

doSecretStuff();}

In the example above, the variable$authenticated indicates that the user isauthenticatedafter all options are successfully checked. Look at the following badexample that violates this rule:

switch ($username) {case "admin": if ($password != "secret")

$loginfailed = true;break;

case "fred" : if ($password != "barney")[...]default: $loginfailed = true;

}if ($loginfailed)

showLoginForm();else

doSecretStuff();

The unclear structure and thedefault: $loginfailed = true statementmake it less obvious that this snippet of script does not work at all. A valid user-name combined with any password will provide access to the secret part. The problemis that the variable$loginfailed

1. is not initialized,

2. cannot be properly initialized, and

3. the default value is used to grant access (in PHP, uninitialized variables aretreated as0, "" , false or NULL).

7 Use well-known security implementations

Programmers should not try to reinvent the wheel and come up with a (more or muchless) bright solution for problems that have already been solved. Basic authenticationfeatures are already implemented in PHP (PHP_AUTH, only available if PHP runs as anApache module); restricted directory access can be implemented using.htaccessmethods on Apache servers. Unfortunately, both transfer the passwords in plain text.

To prevent plain text passwords from being sent over the network, encryption andhash functions can be used. E.g., implementations of the MD5 algorithm are freelyavailable on the Internet as JavaScript functions (MD5 is part of the PHP library aswell).

Page 6: Php Security Iissues

6

8 Status

This document is still under development. If you have any comments, suggestions, orif you found errors, please do not hesitate to send me email.

©Thomas Böhne, May 2003

9 About this document

This document was automatically generated from a XML source usingxsltproc andtbook (seehttp://sourceforge.net/projects/tbookdtd ).