Securing PHP

How to install PHP securely with Apache on a Linux server.

by Mike Peters, August 2004
(Originally published on Linux.com)

Introduction

PHP is a popular scripting language used to create dynamic websites on millions of servers worldwide. Serving dynamic pages means giving users access to commands, files and network connections on the server opening up many potential security risks. We can significantly reduce these risks by correctly configuring the server but it must be remembered that there is always a level of responsibility for the programmer to make sure his scripts are secure.

In a previous article I looked at installing Apache in a chroot jail. This article first looks at the extra steps needed to add PHP to that setup and goes on to discuss how to run PHP securely on your server.

Different Methods of Install.

There are several different ways to install PHP on a server, either as a CGI interpreter or an Apache module. Each method has its own advantages and disadvantages making it more suitable to one situation over another.

Installing PHP as a CGI interpreter offers the opportunity to have different PHP processess running as different user IDs under different virtual hosts, using Apache's suEXEC. However this method of installation may be slow and is fraught with risks if mis-configured.

Alternatively, we can install PHP as an Apache module, which is the method we will concentrate on in this article. When installing as an Apache module you have a further choice, whether to compile as a static or dynamic Apache module. A static module has the advantage of increased performance but when you wish to upgrade, you have to recompile both PHP and Apache. With a dynamic module, you lose some performance and you need to have your Apache compiled to suport dynamic modules requiring the mod_so module. Patching and upgrading PHP, however, is much easier when installed as a dynamic module. Your choice will depend upon your particular needs, specifically, the importance of performance over the convenience of an easy upgrade.

Compiling Apache and PHP.

For a detailed description of installing Apache, refer to my earlier article, Securing Apache. In this article I will only look at the extra considerations needed to add PHP to that setup.

When deploying PHP on your web server, one further consideration worth making, is to include mod_security in you Apache installation. This will provide you with an extra defence against Cross Site Scripting and SQL injection attacks.

Static Module

If you are compiling PHP as a static module you will need to compile it before Apache with

./configure --with-mysql=/usr/local/mysql --with-apache=/path/to/apache_source --enable-safe-mode
    make
    su
    make install

Extract the mod_security source and copy apache1/mod_security.c from the mod_security source to src/modules/extra in your Apache source directory.

Now you can compile Apache as normal adding the options --activate-module=src/modules/extra/mod_security --enable-module=security --activate-module=src/modules/php4/libphp4.a to configure.

Dynamic Module

To install PHP as a dynamic module, install Apache first and compile your PHP source with

./configure --with-mysql=/usr/local/mysql --with-apxs=/usr/sbin/apxs --enable-safe-mode
    make
    su
    make install

To install mod_security as a dynamic module, extract the sources and cd mod_security-1.8.3/apache1. As root run apxs -cia mod_security.c to install.

Whichever method we are using to install PHP, only enable the least amount of options needed for your needs. Including more functionality means increasing the risk of vulnerabilities. Additionally, especially in a multiuser environment, it is strongly recommended you use the --enable-safe-mode option (Safe Mode is discussed in detail below).

Install in Chroot environment

In order to Chroot your server, you should refer to my earlier article, Chrooting Apache. Here I will only outline the details needed to add PHP to that environment.

If you compiled PHP and mod_security as dynamic modules you need to copy the modules to your chroot.

    cp /usr/libexec/apache/libphp4.so /chroot/httpd/usr/libexec/
    cp /usr/libexec/apache/mod_security.so /chroot/httpd/usr/libexec/
    

You will also need libmysqlclient and some other nescessary libraries.

    cp /usr/local/mysql/lib/mysql/libmysqlclient.so.12 /chroot/httpd/usr/lib/
    cp /usr/lib/libm.so.2 /chroot/httpd/usr/lib/
    cp /usr/lib/libz.so.2 /chroot/httpd/usr/lib/
    

If you have installed PHP with additional features enabled you may need more libraries. Check with ldd /usr/libexec/apache/libphp4.so (if you installed as a dynamic module ldd /usr/local/bin/httpd if static).

Now we need to make a /tmp directory in our /chroot:

    mkdir /chroot/httpd/tmp
    chown root.root /chroot/httpd/tmp
    chmod 1777 /chroot/httpd/tmp
    
Finally copy the PHP configuration file:

cp /etc/apache/php.ini /chroot/httpd/etc/

Configure Apache to Handle PHP

Add the following lines to httpd.conf to enable PHP.

    LoadModule php4_module        libexec/libphp4.so
    AddModule mod_php4.c
    AddType application/x-httpd-php .php
    AddType application/x-httpd-php .php3
    AddType application/x-httpd-php .inc
    AddType application/x-httpd-php .class
    

The AddType directives ensure that any files with the extensions .php, .php3, .inc and .class are processed as PHP scripts. Many programmers use *.class and *.inc to name external files included by their scripts. Without these directives these files would be displayed as plain text in the client's browser, potentially revealing passwords or other sensitive data.

Another way of acheiving the same affect is to use the Files directive to deny access to files matching a particular pattern. For example the entry

    <Files ~ "\.inc(.php)?$"> 
      Order allow,deny 
       Deny from all 
       Satisfy All 
    </Files>
    

will deny access to all files ending in .inc or .inc.php meaning they could only be accessed using include() or require() in a script.

If you don't want it to be immediately obvious that you are running PHP on your server you can use a different extension for your scripts. Using AddType application/x-httpd-php .dhtml will have Apache interpret files having the .dhtml extension as PHP. It should be noted that this is a fairly superficial measure as there may be other ways users can ascertain that you are using PHP.

Defending Against Injection Attacks With mod_security

mod_security is an intrusion detection and prevention engine for web applications which operates as an Apache module. It is used to sniff GET and POST requests to the server, rejecting potentially malicious requests according to the current configuration.

In order to enable the module we need to add the following lines to our httpd.conf file:

    LoadModule security_module        libexec/mod_security.so
    AddModule mod_security.c
    

To configure the module to scan GET and POST requests we need to add:

    <IfModule mod_security.c>
        # Turn the filtering engine On or Off
        SecFilterEngine On
        
        # Make sure that URL encoding is valid
        SecFilterCheckURLEncoding On
        
        # Unicode encoding check
        SecFilterCheckUnicodeEncoding On
        
        # Only allow bytes from this range
        SecFilterForceByteRange 0 255
        
        # Only log suspicious requests
        SecAuditEngine RelevantOnly
        
        # The name of the audit log file
        SecAuditLog logs/audit_log
        # Debug level set to a minimum
        SecFilterDebugLog logs/modsec_debug_log    
        SecFilterDebugLevel 0
        
        # Should mod_security inspect POST payloads
        SecFilterScanPOST On
        
        # By default log and deny suspicious requests
        # with HTTP status 500
        SecFilterDefaultAction "deny,log,status:500"
        
    </IfModule>
    

The comments in the code should be enough to explain what each directive does. This is the most basic configuration possible but will provide the following advantages:
Remove multiple forward slashes (//).
Remove self-referenced directories (./).
Perform URL decoding.
Replace null bytes (%00) with spaces.
URL encoding validation.
Unicode encoding validation.
Byte range verification, where only certain character values are allowed as part of a request.

We want to use mod_security particularly to prevent SQL and Cross Site Scripting (XSS) attacks. In order to do so we add the following lines within the IfModule block given above:

    SecFilter "\.\./"
    SecFilter "<(.|\n)+>"
    SecFilter "'"
    SecFilter "\""
    

The first line will prevent attempted directory traversal attacks, which are attempts to access files outside the directories containing the current web site. The second detects < and > in requests, causing the server to reject requests containing HTML tags, helping to protect against XSS attacks. The next two lines filter single and double quotes respectively, making SQL injection attacks difficult.

It should be noted that this method means that any of these characters submitted through HTML forms will cause the request to be denied. Programmers will have to convert forbidden characters to their appropriate special tag, (ie &gt;, &lt;, &quot; etc.) using Javascript before forms are submitted to the server.

Less strict detection of XSS attacks can be achieved by using SecFilter "<[:space:]*script" This will only check for the <script tag but will allow other HTML tags.

Safe Mode

One of the major concerns of PHP security in a multi-user environment is that scripts are run under the user ID of the server. This not only means that PHP users have access to whatever resources the server has access to, but that all PHP scripts have access to one another as they all operate with the same UID.

Architecturally this problem is best solved at the OS level, and, although various solutions exist there currently is no entirely satisfactory solution. In the absence of a satisfactory solution, the PHP developers introduced safe_mode.

Safe mode ensures that any file which is being accessed by a script is owned by the owner of the script itself. In order to enable Safe Mode PHP must first be compiled with support enabled, as described above, and the line safe_mode = On must be set in php.ini. When Safe Mode is turned on, a slightly more relaxed version of safe mode can be enabled by setting safe_mode_gid = On. With safe_mode_gid set to On, PHP will compare, not the user ID, but the group ID of the script and the accessed files.

There are a number of further options which can be set in Safe Mode the most important of which are:

safe_mode_exec_dir = /some/dir - function calls such as system() which execute binaries on the server, can only execute those programs present in the specified directory.

safe_mode_allowed_env_vars = PHP_ - this option ensures that users can only set the values environment variables beginning with the PHP_ prefix. You can set this prefix to whatever you want (even multiple values, using a comma delimitted list). However, it is most important that the option is not left blank as this means that users may set any environment variable they wish.

Other PHP Configuration Options

Asides from Safe Mode there are a number of other security related options which can be set in the php.ini configuration file.

open_basedir = /some/dir - limits the files which can be opened and read by PHP to the specified directory tree. Using . as the directory will limit PHP to the directory in which the current script is contained. Multiple directories can be specified seperated by a colon. The default value of this option is to allow access to any directory.

disable_functions = function1,function2 - allows the administrator to forbid the use of specific functions. Functions should be listed by name in a comma seperated list.

expose_php = Off - normally PHP will expose the fact that it is installed on a server by adding information to the headers. Setting this directive to Off prevents this.

display_errors = Off - setting this option to Off will prevent PHP from outputting errors to the clients browser. Such errors could reveal important configuration information, such as file paths and database schema, about the server. It is strongly recommended you turn this feature off on a production server.

log_errors = On - turn this feature On to have errors logged to a file. The log file to be used is specified by error_log below.

error_log = /var/log/php_errors - The file to write error messages to.

register_globals = Off - prevents registering ENVIRONMENT, GET, POST, Cookie and Built-in variables as globals. Having these values as globals makes accessing them much easier for programmers but greatly increases the security risk if code is not well thought out. It is strongly recommended that you set this value to Off (the default value is Off in versions 4.2.0 onwards).

Summary

The very nature of running dynamic web pages on a server makes it a risky prospect. The methods discussed here will decrease the risk of a break in and reduce the potential damage caused by a breach. As always, it is important that you remain informed about new vulnerabilities when they arise and apply security related patches as they become available. Nor should you neglect the security of the underlying web server and Operating System. Finally, you should remember that a portion of the responsibility for the overall system security depends upon the web application itself.