PHP Security - Securing the environment

By Paladion

January 15, 2006

We are starting a new in-depth article series where we will be covering practical suggestions on improving security in applications and securely coding in various programming languages. In this first article we will cover the environment around PHP.

We are starting a new in-depth article series where we will be covering practical suggestions on improving security in applications and securely coding in various programming languages. In this first article we will cover the environment around PHP.

PHP has comes a long way from being the Personal Home Page tools that Rasmus had created way back in 1995, to become arguably the most popular web programming language. There are countless developers who have coded in PHP at least once, either for personal projects or for enterprise applications. The reason — ease of use, quick start ability, scalability, performance and zero setup costs. It has that ah-hah factor, the same satisfaction you get from playing easy-to-win computer games. At the end of a game, you feel flattered and at the top of the world. Unfortunately, in reality, easy generally means less secure. And less secure means more headaches later on (no, not the headaches you get after playing games excessively). So let's get down to business to save a few of them.

PHP configuration file

PHP is primarily a web programming language and many a time, it needs to be configured to go well with the web server it is coupled with. php.ini helps you setup the perfect environment for running PHP on your web server. When you install PHP, two configuration files are available. It also provides some security features to secure PHP. Here I take the key security directives in php.ini


This directive from the php.ini is one directive that most developers love and security professionals hate. If you set register_globals to 'On', form variables, cookies, session variables that are passed on through a request are automatically declared as global variables in your script and available with values. Things become so easy. Pass on a GET request with a value, for example,, and the variable $fruit with its value is directly available in your program. So what's the issue, you say. Isn't that nice? Look at the sample code below:

if ($username == "foo" && $passwd == "baroffoo") {
$login = true;
if ($login) {
$_SESSION["username"] = $username;
// Rest of the code ...

It looks fine, alright. Setup a session only of a user is successfully authenticated with the username and password given in the if clause. But with register_globals enabled, it could never be worse. $login is an internal variable. A malicious user can easily its value to true by simply passing it through the GET request like this: . The value of the passed parameter is available in the variable $login at the start of the script. There! you don't need any password to authenticate. You've managed to crack into an application by simply manipulating the URL.

The recommendation is to switch off register_globals by setting its value in the php.ini:

register_globals = Off

When register_globals is disabled, all values sent to the script are available in what are called as superglobal variables. So GET request variables are stored in a variable $_GET, POST variables in $_POST, cookies in $_COOKIE and others in $_SERVER. The above code will turn to something like this:

if ($_POST["username"] == "foo" && $_POST["passwd"] == "baroffoo") {
$login = true;
if ($login) {
$_SESSION["username"] = $_POST["username"];
// Rest of the code ...

So now if a URL like this: is accessed, the parameter 'login' will be available as $_GET["login'] instead of $login in the script.

If you still need the register_globals feature for specific scripts, you can enable it on a directory basis using the Apache htaccess configuration options. Enter this directive in the .htaccess file of the web directory:

php_flag register_globals on

Safe Mode

Safe Mode was introduced in PHP to tackle the problems arising out of a shared server environment. It disables access to other users' files and scripts and also to dangerous functions like shell_exec() and other functions . Any violations to the safe_mode restrictions are logged to the server's error log.

When safe mode is enabled, PHP will check if the file that is being accessed has the same owner as the script that is accessing the file. This prevents someone to access others' files on the system. Any system call (exec, system, passthru) can access only the executables specified in the safe mode configuration. Environment variables are also protected from being overwritten when this mode is enabled.

safe_mode = On
safe_mode_gid = On

safe_mode does a default UID check when opening files. We can relax this to GID checks using the safe_mod_gid directive.

safe_mode_include_dir = /usr/local/php/include

This specifies the directory which does not require the UID/GID checks to be done. All files under this directory are allowed access to.

safe_mode_exec_dir = /usr/local/php/bin

Execution functions like system() will not run external programs unless they are in the directory in the safe_mode_exec_dir directive.

safe_mode_allowed_env_vars = PHP_

This will allow modification of environment variables prefixed with 'PHP_' using the putenv() functions. If this directive is left blank, all environment variables can be modified by the script.

safe_mode_protected_env_vars = LD_LIBRARY_PATH

You can specify specific environment variables that cannot be modified in any circumstance, not even with the use of the safe_mode_allowed_env_vars directive.

However note that safe_mode is not a complete solution for the problem of file access. An attacker may still be able to access session data stored in '/tmp', for example. It does improve things but is bad protection by itself alone. The problem is more of an architectural issue.

Suppressing errors

PHP, by default, throws out messages to the browser if it encounters errors. This is good only for the development environment. On a production website, you wouldn't want to reveal your variable names and file paths to the user, do you? The best practice would be to capture errors to a log file which can then be sifted through separately. Set the following parameters to stop errors from being shown in the web browser:

error_reporting = E_ALL

The E_ALL value for error_reporting will enable reporting of all errors.

display_errors = Off
display_startup_errors = Off

Disable the display of all errors to the browser.

html_errors = Off
log_errors = On
error_log = /var/log/php/errors

Enable logging of errors and save them to the file mentioned. html_errors = Off will disable the inclusion of HTML tags in the error messages. The tags are not useful while logging to a text file. We would rather want clean error messages in text.

Other related directives

disable_functions = "dl,phpinfo,shell_exec,passthru,exec,

Use the disable_functions directive to stop the use of certain dangerous functions that applications should not have access to. phpinfo() is the biggest culprit of leaking out large amount of information on PHP extensions, server information, environment, etc.

open_basedir <integer>

This limits the files that can be opened by PHP to the path specified here. Any file outside this path will not be accessible to PHP. This is different from setting permissions on directories. If a file is accessed beyond the specified path, PHP will even refuse to check if it exists or not.

max_execution_time <integer>

This sets the maximum time in seconds a script is allowed to process. Beyond this, the script is terminated by the PHP parser and frees up CPU resources. This helps block badly written scripts from taking all the CPU resources of the web server.

post_max_size <integer>

This sets the maximum allowed size for data sent to the web server using the POST method. Unless your application requires the user to upload large files, you should set this to a low value of around 2MB. The default value is 8MB.

memory_limit <integer>

This prevents poorly written PHP scripts from allocating large amounts of memory on the server. Ensure that the value is always greater than the post_max_size directive.

expose_php <boolean>

This prevents the PHP string from appearing the server string (web server signature)

Running PHP as CGI binary

With the Apache web server, PHP can be run in two ways: as a module or as a CGI script. If run as a module, PHP will run with the same user permissions as the web server (typically the user 'nobody'). Often this creates problems if a PHP application wants to write / create files in directories belonging to other users. PHP would not have access rights to these directories. Escalating Apache's permissions to root level is a dangerous mistake that many do, to work around this problem.

To enable PHP as a CGI binary, you need to compile and install the PHP CGI binary. And add the following parameters to the Apache configuration:

ScriptAlias /php-cgi /usr/local/php-cgi/bin/
Action php-cgi /php-cgi/php
AddHandler php-cgi .php .php4 .php5

And you are ready to go. (/usr/local/php-cgi/ is the directory where the CGI binary is installed).

Securing Apache

In addition to securing PHP, you can strengthen Apache web server also. This will help make the whole environment around the application more robust and resistant to attacks.


The mod_security Apache module is basically an application firewall that sits between any web application and the user. It blocks certain kinds of web requests which may be considered as attacks on the application. It can do request filtering, prevent evasion techniques used by attackers, POST payload analysis, audit logging and a whole lot of attack prevention measures .

Installing mod_security is quite straightforward. The hard part comes with the configuration. The configuration of mod_security is done in Apache's configuration file. You can start with a relaxed configuration and then build on it making it more restrictive. You can read an excellent primer from the creator of mod_security and see the complete documentation on


mod_evasive is an evasive maneuvers module for Apache. It provides protection against HTTP DoS attacks, DDoS attacks and brute force attacks. It temporarily blocks out client IP addresses which have been requesting a page more than a few number of times each second. However, it is only effective to the limit of the bandwidth and CPU consumption. The author recommends to separate it from the main application web server. You can find an excellent how-to for the module at the

Apache chroot jail

Chroot on Unix operating systems is an operation which changes the root directory (/). This applies only to a specific process and its dependencies. It is a way to sandbox any application into a secure area on the file system. When a program is 'chrooted', it cannot access any file or system library outside the specified path. If you are running applications on a web server which also stores other critical data and you do not want the web service to access any file outside a specific directory, you can chroot the web server.

Note that chrooting Apache will not prevent Apache from being vulnerable to attacks. It would only prevent an attacker from accessing other data on the system through a compromised Apache web service. So use chroot jails only when you are extremely paranoid about the health of the system and the data it stores.

The details of how to actually set up a chroot jail are beyond the scope of this article. You can read an excellent article on chrooting Apache on

Stay there. The next article will talk about secure PHP coding and improving the robustness of PHP applications.

Other resources

  1. Security Manual -
  2. Book: Essential PHP Security by Chris Shiflet
  3. PHP Security Consortium
  4. Hardened PHP

Tags: Technical