Programming PHPProgramming PHPSearch this book

Chapter 12. Security

Contents:

Global Variables and Form Data
Filenames
File Uploads
File Permissions
Concealing PHP Libraries
PHP Code
Shell Commands
Security Redux

PHP is a flexible language that has hooks into just about every API offered on the machines on which it runs. Because it was designed to be a forms-processing language for HTML pages, PHP makes it easy to use form data sent to a script. Convenience is a double-edged sword, however. The very features that let you quickly write programs in PHP can open doors for those who would break into your systems.

It's important to understand that PHP itself is neither secure nor insecure. The security of your web applications is entirely determined by the code you write. For example, take a script that opens a file whose name was passed as a form parameter. If you don't check the filename, the user can give a URL, an absolute pathname, or even a relative path to back out of the application data directory and into a personal or system directory.

This chapter looks at several common issues that can lead to insecure scripts, such as filenames, file uploads, and the eval( ) function. Some problems are solved through code (e.g., checking filenames before opening them), while others are solved through changing PHP's configuration (e.g., to permit access only to files in a particular directory).

12.1. Global Variables and Form Data

One of the most fundamental things to consider when creating a secure system is that any information you didn't generate within the system should be regarded as tainted. You should either untaint this data before using it—that is, ensure that there's nothing malicious in it—or limit what you do with it.

In PHP, however, it's not always easy to tell whether a variable is tainted. When register_globals is enabled in the php.ini file, PHP automatically creates variables from form parameters and cookies. Poorly written programs assume that their variables have values only when the variables are explicitly assigned values in the program code. With register_globals, this assumption is false.

Consider the following code:

<?php
  if (check_privileges( )) {
    $superuser = true;
  }
  // ...
?>

This code assumes that $superuser can be set to true only if check_privileges( ) returns true. However, with register_globals enabled, it's actually a simple matter to call the page as page.php?superuser=1 to get superuser privileges.

There are three ways to solve this problem: initialize your variables, disable register_globals in the php.ini file, or customize the variables_order setting to prevent GET, POST, and cookie values from creating global variables.

12.1.1. Initialize Variables

Always initialize your variables. The superuser security hole in the previous example wouldn't exist if the code had been written like this:

<?php
  $superuser = false;
  if (check_privileges( )) {
    $superuser = true;
  }
  // ...
?>

If you set the error_reporting configuration option in php.ini to E_ALL, as discussed in Chapter 13, you will see a warning when your script uses a variable before it initializes it to some value. For example, the following script uses $a before setting it, so a warning is generated:

<html>
  <head>
    <title>Sample</title>
  </head>

  <body>
    <?php echo $a; ?>
  </body>
</html>
Warning:  Undefined variable:  a in /home/httpd/html/warnings.php on line 7

Once your script is in a production environment, you should turn off public visibility of errors and warnings, as they can give a potential hacker insight into how your script works. The following php.ini directives are recommended for production systems:

display_errors = Off
log_errors = On
error_log = /var/log/php_errors.log

These directives ensure that PHP error messages are never shown directly on your web pages. Instead, they are logged to the specified file.

12.1.2. Set variables_order

The default PHP configuration automatically creates global variables from the environment, cookies, server information, and GET and POST parameters. The variables_order directive in php.ini controls the order and presence of these variables. The default value is "EGPCS", meaning that first the environment is turned into global variables, then GET parameters, then POST parameters, then cookies, then server information.

Allowing GET requests, POST requests, and cookies from the browser to create arbitrary global variables in your program is dangerous. A reasonable security precaution is to set variables_order to "ES":

variables_order = "ES"

You can access form parameters and cookie values via the $_REQUEST, $_GET, $_POST, and $_COOKIE arrays, as we discussed in Chapter 7.

For maximum safety, you can disable register_globals in your php.ini file to prevent any global variables from being created. However, changing register_globals or variables_order will break scripts that were written with the expectation that form parameters would be accessible as global variables. To fix this problem, add a section at the start of your code to copy the parameters into regular global variables:

$name = $_REQUEST['name'];
$age  = $_REQUEST['age'];
// ... and so on for all incoming form parameters


Library Navigation Links

Copyright © 2003 O'Reilly & Associates. All rights reserved.