Securing PHP

73

Author: Mike Peters

PHP is a popular scripting language used to create dynamic Web sites 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 reduce these risks significantly by
correctly configuring the server, but programmers should also be responsible for making sure their scripts are secure.

In a previous article we looked at installing Apache in a chroot jail. This article 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 installation methods

You can install PHP on a server either as a CGI interpreter or an Apache module. Each method has its advantages and disadvantages.

Installing PHP as a CGI interpreter offers the opportunity to have different
PHP processes 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 misconfigured.

Alternatively, you 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 it 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 support dynamic modules
requiring the mod_so module. Patching and upgrading PHP, however,
is much easier when it is 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, Chrooting Apache. In this article we will look only at the extra considerations needed to add PHP to that setup.

When deploying PHP on your Web server, one further consideration is to include mod_security in you Apache installation to provide you with an extra defense against cross-site scripting and SQL injection attacks.

Static Module

If you are compiling PHP as a static module, you need to compile it
before compiling Apache with the commands:

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

Copy apache1/mod_security.c from the mod_security source to src/modules/extra in your Apache source directory, then 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, then compile your PHP source with the commands:

./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 you use to install PHP, enable only the fewest options you need. Including more functionality means increasing the risk of vulnerabilities. Additionally, especially in a
multiuser environment, I strongly recommend you use the
--enable-safe-mode option (Safe Mode is discussed in detail below).

Install in a chroot 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 necessary 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, or ldd /usr/local/bin/httpd if static.

Now make a /tmp directory in /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 to achieve 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>

denies 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. Mind you, this is a fairly superficial
measure, as there are 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
that operates as an Apache module. It sniffs GET and POST requests to
the server and rejects potentially malicious requests according to the current
configuration.

In order to enable the module, add the following lines to the httpd.conf file:

LoadModule security_module        libexec/mod_security.so
AddModule mod_security.c

To configure the module to scan GET and POST requests, 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 prevents 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 and helping to protect
against XSS attacks. The next two lines filter single and double quotes
respectively, making SQL injection attacks difficult.

Using this filtering 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 an appropriate special tag (i.e. &gt;,
&lt;, &quot; etc.) using JavaScript before the forms are submitted to the
server.

You can implement less strict detection of XSS attacks by using SecFilter
"<[:space:]*script"
. This checks only for the <script tag but
allows 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 operating system level, and, although
various solutions exist, there is no entirely satisfactory solution. In one’s absence, the PHP developers introduced safe_mode.

Safe mode ensures that any file that is being accessed by a script is owned
by the owner of the script itself. In order to enable Safe Mode, you must compile PHP with support enabled, as described above, and include the line
safe_mode = On 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
compares not the user ID, but the group ID, of the script and the accessed
files.

There are a number of further options you can set in Safe Mode. The most
important are:

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

safe_mode_allowed_env_vars = PHP_ — Users can set only the values environment variables beginning with the PHP_ prefix. You can set this prefix to whatever you want (even multiple values, using a comma-delimited list). However, don’t leave the option
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 you can set
in the php.ini configuration file:

open_basedir = /some/dir — limits the files that PHP can open and read
to the specified directory tree. Using . as the directory limits
PHP to the directory in which the current script is contained. You can specify multiple
directories by separating them with colons. 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-separated list.

expose_php = Off — Normally PHP exposes 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 prevents
PHP from outputting errors to clients’ browsers. Such errors could reveal
important configuration information about the server, such as file paths and database schema. I strongly recommend 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:

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. I strongly recommend 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 decrease the risk of a break-in and reduce the potential damage caused by a breach. As always, stay informed about new vulnerabilities as 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, remember that a portion of the responsibility for the
overall system security depends upon the Web application itself.