PHP Security Measures
Software Evaluation
Andre S. Burton
February 24, 2003

 

Introduction
PHP is a recursive acronym for 'PHP Hypertext Preprocessor'. It is an interpreted language - it is executed at runtime; no executable or object file is needed to run it. It is commonly compiled with Apache as a web module or included as a CGI application. Some of its advantages include:

  • 'openess' - it is open source software; since it is constantly under the vigilant eyes of the web community at large, its vulnerabilities are exploited leading to quick fixes and great support
  • fast - since it's interpreted, it runs on the fly
  • simple - the novice web developer can pick up its syntax in a matter of days
  • highly functional - the functions built-in to PHP provide a high-level of abstraction making it less cumbersome
  • portability - under the default configuration, code may be easily transported from one system to another without worry of incompatibility

Despite its inherent advantages, like any software, it comes with bugs and weaknesses. Whether you are an administrator or programmer, security should be a priority as 'hackers' are looking for ways to exploit the weaknesses of both the system and application code. As with most web programming software, security measures must be examined at the three possible levels of vulnerability: actual software or engine, system/administrator, and/or user/programmer.

Software-Level
PHP's new releases incorporates increased security measures and fixes and enhancements. One of the main goals PHP supporters is to see PHP in wide use; a factor largely determined by reliability.

Reliability, for the most part, is largely affected by an application's security flaws and usefulness/ease of use. In PHP's case, the two seem inversely proportional; trade-off must be made. Approaching the problem top-down, we see the problems start from the software-level - inherent software capability - and move down into the lower tier - the programmer's ability to create secure code. Let's start at the software-level.

PHP's website, http://php.net, always posts vulnerability findings and solutions to those findings in a timely manner. The latest vulnerability advisory, exposes vulnerabilities in versions 4.2.0 and 4.2.1 for all platforms (http://www.php.net/release_4_2_2.php). According to this advisory, "An intruder may be able to execute arbitrary code with the privileges of the web server. This vulnerability may be exploited to compromise the web server and, under certain conditions, to gain privileged access." - privileged access system administrators have nightmares about.

Many times as a programmer, administrator or privileged access is something not afforded to you as a user of the software- sometimes for your own good. Although new vulnerabilities are exposed and fixes are posted, you have little control over how or when the problems will be fixed. As a security-aware programmer, it may be in your best interest to contact your system administrator and make 'kind' suggestions and/or recommendations for change; recommendations that are at the mercy and decision of the people who run the system.

System-Level
System administrators want to keep their system running. Within the context of PHP, there are several factors that may prevent this from happening. With this in mind, admins must make decisions on what features they want to make available to their users.

As a semi-admin, I do not want to fix the problem by editing the source code. Instead, I find it easier in many cases to restrict or limit what features are available, and with PHP, this is easily done with configuration. There are commonly recommended values for certain configuration options, but before I talk about those, it would be more useful to look at what types of security mistakes programmers make.

Programmer-Level
What is the programmer's motivation for secure code? Mostly data integrity. Who wants their data manipulated, deleted or stolen? There are common mistakes that many programmers make, risking the integrity of their data as 'hackers' are constantly trying to break people's code(s).

Global Variables
The keyword variable defined: "A location in memory, referenced by an identifier, that contains a data value that can be changed." [4] In PHP, user input is usually converted to variables upon form submission.

The tag <input type="text" name="color"> renders:


Any data entered into this box once the form is submitted is converted into a PHP variable. Once we have that data in variable form, we can print it out, save it to a database, etc. In the example, we would reference that variable as $color, as the $ denotes a variable. If we wanted to print that variable out to the screen,

<?
print("$color <br>");
?>

would suffice. Now this assumes the form is submitted using the POST method (<form action="someprogram.php" method="POST">). What if a user of your program added a ?color=something_detrimental to the end of your URL (making it GET)? Adding variable name-value pairs or changing variable values is a common way to break ones code. This is especially the case when the variable is not validated.

To give you a better example, let's look at an oversimplified case where we want to authenticate a user using the following code:

Example 1.1
<?php
//must be trying to login
if($password == "s0m3passw0rd"){
  //set authentication level to true
  $authenticate = 1;
}


//see if the user has already been authenticated
if($authenticate == 1){
  //print out user stuff
  printUserTools();
}

?>

Here it is assumed the user will only use the program as intended. What if the user typed in:

someprogram.php?authenticate=1

Now the user is authenticated and never needed to guess the password (even if SSL was used to encrypt the data).

General rule: PHP scripts cannot trust any variable that has not been explicitly declared. PHP variables do not have to be declared; they are pretty much declared when they're being used. This is advantageous as programmers don't have to worry as much about data typing or the extra line of code. However, since variables are created as needed, validating variables becomes highly important as users can seemingly create any variable at any time.

One common way to prevent GET variable creation on the user-end (for programmers) is to only accept variables found in the $HTTP_POST_VARS array. $HTTP_POST_VARS is an associative array meaning for each index, a literal value corresponds to a value assigned to a particular variable in the program. This is an environmental variable so it should be available to you as a programmer regardless of platform. If I only wanted the variable $authenticate to come from a 'POST' ed form, I would use like:

Example 1.2
<?php
//must be trying to login
if($password == "s0m3passw0rd"){
  //set authentication level to true
  $authenticate = 1;
}


//see if the user has already been authenticated
if($HTTP_POST_VARS['authenticate'] == 1){
  //print out user stuff
  printUserTools();
}

?>

This of course is not the most efficient way to authenticate a user, but serves its purpose in showing how to get around the problem of accepting variables manipulated from the URL.

We also want to validate variables based on their type...not just the way they were submitted. If you have to generate a database query based on user input, you want to make sure the input is of a certain type.

Example 1.3
<?php
include "dbConnect.php"; //DB Login info

$Query = "select * from user where uname='$user'";

$dbResult = mysql_query($Query);
while($row = mysql_fetch_row($dbResult)){
  print("$row[0] $row[1]: $row[2]<br>\n");
}

?>

What if we wanted $user as a result of a GET request? In this case, a user can change the value for the $user variable within the URL. What if the user substituted this string for the $user variable:

$user = "drop db database";

MySQL nested query capability is version dependent, but assuming the correct version, the variable $Query is now assigned:

"select * from user where uname='drop db database'";

Although the entire query may be erroneous, nested queries start at the inner-most queries and execute them first. Assuming you have drop database capability, dropping the entire database will cause you, the programmer, to lose all your data - probably forever. Place restrictions on the variable. Knowing that $user is between 8 to 14 characters long and contains no spaces or special characters like %, can be the basis for validation.

Example 1.4
<?php
include "dbConnect.php"; //DB Login info

function isValidVar($str){
  $mark = 0;
  $mark = (!ereg(" " | '%') && (strlen($str) >= 8) &&  strlen($str) <= 14))

  return ($mark);
}

if(isValidVar($user)){
  $Query = "select * from user where uname='$user'";

  $dbResult = mysql_query($Query);
  while($row = mysql_fetch_row($dbResult)){
    print("$row[0] $row[1]: $row[2]<br>\n");
  }
}
?>

File handling
Huge issues here. Let's start with include and require. Both functions are aliases of one another and can be used somewhat synonomously. The statement

include("include.php");

is commonly used to include class or reusable function definitions. Include basically takes the contents of include.php and text between PHP tags as PHP code. What if the included file was from another server - include("http://somehost.com/somefile.php")? Big risk. You are basically assuming that the code at that location will always be trustworthy and secure; not a good assumption to make given cost-benefit analysis. Somebody could have easily replaced that file with a PHP program to break yours...

fpassthru($FP) simply renders all text in $FP as text. Nothing between PHP tags will be rendered as code. Allowing a user to put a PHP file on your server in this function, would allow him to see code unrendered. Here, he can attack further vulnerabilities of any given program at will.

Similiarly, file extensions can be problematic. Pre-PHP4 days, many programmers used different extensions for PHP files: .php3, .php4, etc., although this is less of a problem nowadays. Putting the version number at the end of the extension made code version dependent. Compiled PHP is only given files that are recognized as PHP code; therefore, a PHP file with a version-specific extension on a platform only supporting or recognizing current version extensions may render files as plain text. Once again, code is exposed unrendered (although not necessarily executable).

Common mistake: do not give included files extensions that will not hide the code in a web-accessible directory. In my personal web-directory, let's assume I have a file called include.inc. In this file, I have functions I wish to reuse in various parts of other programs. Including this file as includes or requires, I now have multiple files dependent on a file whose code can be read by any user. A user can now take this code and exploit its vulnerabilities in other programs that use it.

System Execution
One may execute sytem-level commands using the system or exec functions built into PHP. The system call "tries to automatically flush the web server's output buffer after each line of output if PHP is running as a server module"; i.e. assuming default PHP configuration, any Linux command executed at the command line may be executed with system(string command). If I wanted to execute less [filename],

system("less dbConnection.php");

The user now has access to the contents of a PHP program that can execute.

A greater threat: what if he wanted to see other private server data?

system("less /etc/passwd");

As harmless as less or more seem, they can be detrimental to the system's security should the wrong files be exposed. Obviously, less is not the only command that can prove useful. As a system admin, there are ways to reduce the number of executable commands using a configuration option called safe_mode; we'll talk about that in the next section.

Quick note: as a programmer are not completely blind to your system's setup. Create a PHP file in a web-accessible directory on your desired host to look at how your host configured PHP by inserting this code:

Example 1.5
<?php
phpinfo();
?>

See this example running on this server.

Back to System-Level Configuration
Now that we've looked at common programmer mistakes, let's look at ways we can control or limit the potential for these types of mistakes from the admin's end. Let me reiterate an important fact with making restrictions here with PHP: with increased security measures comes the sacrifice of programmers finding it less useful. One of the major advantages of PHP is its simplicity - factor strongly correlated with portability. With limitations placed on code, imported code has less of a probability of executing properly. The two most commonly recommended options:

Set register_globals off. The most recommended option. No longer can users switch between HTTP methods. However, it hurts PHP's simplicity.

Set safe_mode on. Does a number of things when turned on: "1. restrict the commands executed by system or exec, 2. restrict which functions can be used, 3. restricts file access based on ownership of script and target file, and 4. kills upload completely." [2]

Installation

If you want PHP installed as a web module, you're going to have to install it with Apache. If you already have Apache installed, you're going to have to reinstall it - it's worth it, don't worry. Here is the basic Linux install for PHP with Apache and MySQL (assuming it's already installed) including the above mentioned 'secure' configuration options:

  1. gunzip apache_2.0.44.tar.gz
  2. tar xvf apache_2.0.44.tar
  3. gunzip php-4.2.2pl1.tar.gz
  4. tar xvf php-4.2.2pl1.tar
  5. cd apache_2.0.44
  6. ./configure --prefix=/usr/local/apache
  7. cd ../php-4.2.2pl
  8. ./configure --with-mysql --with-apache=../apache_2.0.44 --enable-track-vars --enable-safe-mode
  9. make
  10. make install
  11. cp libs/libphp4.a ../apache_2.0.44/src/modules/php4/
  12. cd ../apache_2.0.44
  13. ./configure --activate-module=src/modules/php4/libphp4.a
  14. make
  15. make install

Not as easy as it seems. I had a MySQL client installed...

Resources
There is so much to talk about and so little time to do so - so many topics not covered here. If you're interested, you can find out more at these sites.

General and Code Repositories
[1] PHP's Homepage: http://php.net
PHPBuilder: http://phpbuilder.com
PHPFreaks: http://phpfreaks.com

Security
[2] A Study in Scarlet - Exploiting Common Vulnerabilities in PHP Applications: http://www.securereality.com.au/studyinscarlet.txt
[3] PHPAdvisory.com - http://www.phpadvisory.com/

Books
Core PHP 4
Beginning PHP. Wrox

Reference
[4] Dale, N., Headington, M., Weems, C. Programming and Problem Solving with C++. Jones and Bartlett Publishers. Sudbury, MA. 2000.
Online Dictionary for Computer and Internet Terms: http://webopedia.com