Hardening PHP with Suhosin

30 November -0001

Suhosin is an extremely valuable part of any effort to secure a PHP installation. Many PHP users have long been aware of Suhosin as FreeBSD, OpenSuSE, Debian and Mandriva come with Suhosin preconfigured or available for their PHP distribution. Suhosin is an add-on to PHP that provides additional protections against many attack vectors. For instance, Suhosin will note when a URL variable is overly long (evidence of a buffer overflow attempt or other nastiness) and automatically drop the variable. Suhosin will also protect against buffer overflow attacks, ASCII null characters in variables (including POST and COOKIE variables), directory traversal attacks, some common injection points for string format vulnerabilities, unauthorized remote file inclusion, and many other vectors, such as the one used in a recent PHP worm by disabling preg_replace() /e modifiers.

About Suhosin

There are two components of Suhosin and they can be installed separately or in concert. The two components, the patch and the extension, require two separate installation procedures. Installing the extension is by far easier, but limits some of the functionality. If the patch is installed alone, Suhosin only enables logging features. If the extension is installed alone you can't use predefined constants for configuration. This limits several key functionality. Specifically, without the patch Suhosin cannot fine tune logging, tuning syslog facilities, tuning the syslog priority, adding additional protection to the mail() function headers, and tuning Suhosin responses to violation (default in a block response for all violations). The last limit is especially important to note asNote that all these protections remain in place, but their defaults are used rather than allowing for specific values, which often provide more protection. Additionally, the patch provides:

  • Memory protection against buffer overflows
  • Protection the DTORS (destructors) of Zend hashtables and Zend linked lists. DTORS are commonly used as a mechanism for triggering format string injection vulnerability attacks
  • Protection PHP against format string vulnerabilities
  • Protection against some errors in libc realpath() implementations.

And without the patch these features are not available. For a full list of features see the official Suhosin feature list at http://www.hardened-php.net/suhosin/a_feature_list.html.

Suhosin on Red Hat Based Distros

If you're running a Red Hat based distribution (Red Hat, Fedora, or CentOS) you'll notice the conspicuous absence of Suhosin in your PHP installation. This is a curious circumstance, especially given that these distributions go so far as to include the SELinux. Installing Suhosin on your Red Hat based distribution is relatively painless, and the dividends such an installation pay are by and far away worth any hassle you may encounter.

In order to install Suhosin on a Red Hat based installation you'll have to download the PHP source RPM's as well as the Suhosin binaries, then recompile PHP. The advantage in doing this is that you may be able to install a version of PHP that is more current than the latest available in your RPM repositories. The disadvantage is that once you've done this you can't rely on automatic updates to keep your PHP installation current as the new install won't have Suhosin. Make sure before you start down this route that you have the rpm-build package installed. You can do this using:

# rpm -q rpm-build

If rpm-build isn't installed go ahead and add that to your machine using:

# yum install rpm-build

You'll also need the php-devel packages, so install these in the same manner as rpm-build.

Installation on CentOS 5.x involves quite a few steps. The first is to make sure you've got PHP, any extensions you want, and the PHP sources already installed. You can do this by checking the output from:

# rpm -qa | grep php

If you don't have any or all of the packages that you want installed you can use yum to install them. For instance you could install PHP, CLI, GD, MySQL, IMAP, PDO, PEAR, XML and XMLRPC support with:

# yum install php php-cli php-gd php-mysql php-imap php-pdo php-pear php-xml php-xmlrpc

Once PHP and all the add-ons you want are installed you need to install the PHP source package. Unfortunately the yum utility cannot be used to install source packages so you have to download them by hand. One good place to find source RPMs is off the main CentOS website at http://mirror.centos.org/centos/5/os/SRPMS/. You need to download the source into the /usr/src directory. You can use the following commands to download the source for php 5.1.6-20

# cd /usr/src
# wget http://mirror.centos.org/centos/5/os/SRPMS/php-5.1.6-20.el5.src.rpm

Next you need to install the new source RPM using:

# rpm -ivh php-5.1.6-20.el5_2.1.src.rpm

Note that if you get the error 'error: cannot create %sourcedir /usr/src/redhat/SOURCES' then you have to create the directory 'redhat' to continue the installation using:

# mkdir /usr/src/redhat

If you encounter warnings about the user or group mockingbird not existing and the rpm using root don't worry, these are not errors. The next step is to download the appropriate version of the Suhosin patch from http://www.hardened-php.net/suhosin/download.html. Once the patch is downloaded you'll want to check the MD5 sum of the patch and compare it to the one listed on the distribution website just to be sure the download hasn't been tampered with. Of course if an attacker compromised the Hardened-PHP web server they could tamper with the patch and modify the published MD5 hash. Hardened-PHP also publishes a GPG key public key that can be used to verify the patch signature, but again, an attacker who controlled the web server could change that. It's probably sufficient just to check the MD5 hash value. To download and confirm the MD5 use:

# wget http://download.suhosin.org/suhosin-patch-5.1.6-0.9.6.patch.gz
# md5sum suhosin-patch-5.1.6-0.9.6.patch.gz

Assuming the patch downloads and checks out you want to unpack the zipped patch and move it into the appropriate directory (/usr/src/redhat/SOURCES). Note that we're also renaming the patch to follow convention:

# gunzip suhosin-patch-5.1.6-0.9.6.patch.gz
# mv suhosin-patch-5.1.6-0.9.6.patch redhat/SOURCES/php-5.1.6-0.9.6-suhosin.patch

Next you have to edit the PHP RPM specification file in order to comment out a conflicting package (ecalloc) and add in the new Suhosin patch. Add the following line above the list of patches in /usr/src/redhat/SPECS/php.spec:

Patch0: php-5.1.6-0.9.6-suhosin.patch

And the following line in the '%setup' section:

%patch0 -p1 -b .suhosin

and comment out the lines for ecalloc with a pound sign so the two lines (in the patch section in and the setup section) so they look like this:

# Patch14: php-5.1.6-ecalloc.patch
# %patch14 -p1 -b .ecallo

Next go ahead and rebuild the RPM using:

# rpmbuild -ba redhat/SPECS/php.spec

You may get a lot of dependency errors as a result of this command. Go ahead and add the packages using yum and retry if this is the case. In my case this list was quite long so the update was as well:

# yum install bzip2-devel curl-devel db4-devel expat-devel gmp-devel aspell-devel httpd-devel libjpeg-devel libpng-devel pam-devel libstdc++-devel openssl-devel sqlite-devel zlib-devel pcre-devel libtool gcc-c++ krb5-devel libc-client-devel cyrus-sasl-devel openldap-devel mysql-devel postgresql-devel unixODBC-devel libxml2-devel net-snmp-devel libxslt-devel libxml2-devel ncurses-devel gd-devel freetype-devel

When you issue the rpmbuild command there will be a lot of compiling - take the opportunity to get a cup of coffee. Once the new RPM is built you need to install it. The build process parks all the new RPM's in /usr/src/redhat/RPMS/i386. These all have to be installed, but because they may be the same version as the currently installed packages you have to use the '--force' flag to push the installs. You can install these packages using:

# rpm -ivh --force /usr/src/redhat/RPMS/i386/php*

To check the installation use the PHP CLI with the '-v' flag, and check to make sure your PHP version lists the "Suhosin-Patch" in the version information:

# php -v
PHP 5.1.6 with Suhosin-Patch 0.9.6 (cli) (built: Feb 20 2009 04:24:00)
Copyright (c) 1997-2006 The PHP Group
Zend Engine v2.1.0, Copyright (c) 1998-2006 Zend Technologies

Once the patch is installed you can just install the Suhosin extension quite simply. Alternatively you can simply install the extension, you don't necessarily have to have the patch in place for the extension to work. Installing the extension may be preferable in many circumstances because although it doesn't provide the full functionality of the patch and the extension, it is easier to install, requires fewer additional packages, and doesn't require you to rebuild the PHP binary. To install the extension, as with the patch, you'll need php-devel installed.

# yum install php-devel

Next you have to download the source code for the extension and untar the zipped archive:

# wget http://download.suhosin.org/suhosin-0.9.27.tgz
# tar -xvzf suhosin-0.9.27.tgz

Next you use the 'phpize' command to prep the extension which will give you some information back:

# cd suhosin-0.9.27
# phpize
Configuring for:
PHP Api Version:         20041225
Zend Module Api No:      20050922
Zend Extension Api No:   220051025

Finally you use the standard configure, make, make install routine:

# ./configure
# make
# make install

This should result in the final output of "Installing shared extensions: /usr/lib/php/modules/" indicating the extension has been successfully installed. To use the extension you'll have to enable it so PHP knows about Suhosin. To do this issue the following command:

# echo 'extension=suhosin.so' > /etc/php.d/suhosin.ini

Finally restart your web server to kick off the new configuration:

# /etc/rc.d/init.d/apache restart

You can check whether or not Suhosin was successfully installed quite easily using the PHP CLI with the '-v' flag like so:

# php -v
PHP 5.1.6 with Suhosin-Patch 0.9.6 (cli) (built: Feb 20 2009 04:24:00)
Copyright (c) 1997-2006 The PHP Group
Zend Engine v2.1.0, Copyright (c) 1998-2006 Zend Technologies
    with Suhosin v0.9.27, Copyright (c) 2007, by SektionEins GmbH

If you do decide to take these steps and install Suhosin on your Red Hat based machine without building a new RPM be sure to subscribe to the PHP mailing list to keep current on any future releases that will surely be provided to address security concerns. Subscribing to the PHP Announcements mailing list at http://www.php.net/mailing-lists.php is a good idea for almost any developer, but it especially critical if you decide to compile your own PHP binary as Red Hat based package managers will no longer be able to alert you to your version falling out of sync with security patches and release updates.

Using Suhosin

Once installed there are several configuration choices you'll have to make in order to best utilize the power of Suhosin. Suhosin configuration is controlled in the php.ini configuration alongside other PHP settings. Note again, that if the Suhosin patch is not applied, you may not make use of any predefined constants in the php.ini, which will limit some choices with respect to certain configurations. Using the defaults will provide quite a bit of protection, but there are certain non-standard configurations that may also be appropriate in your environment.

One of the most important configuration options in a new deployment is the "simulation" options. This allows Suhosin to run in a logging only mode. This allows you to test out Suhosin without worrying that it will break critical scripts. To enable this option include the following in your php.ini:

suhosin.simulation = On

Be sure to review your log files once enabling this option, as it will allow you to spot any trouble that Suhosin would have caused if it were in an active mode. Once you've tracked down problem scripts and feel safe with your Suhosin configuration you can reset this value to the default "Off" state by simply deleting the line from your php.ini file.

By default the directory traversal protections are disabled in Suhosin. This protection limits the number of '../' sequences that may be included in a filename. As many attackers will include several of these sequences in order to reach a server root, it might be safe to limit this to four or five. This would allow PHP scripts to reach around inside the web server directory but would prevent, and alert administrators to, many attacks. This configuration can be achieved by adding the following to your php.ini:

suhosin.executor.max_traversal = 4

Another option worth considering is disabling the /e flag in the preg_replace() function. This flag allows PHP code to be evaluated into the replacement and can be used by attackers to inject PHP code into scripts. By default this feature is off, to enable it add:

suhosin.executor.disable_emodifier = On

Similarly you may want to disable the eval() function altogether, especially if you're not using it in any of your scripts. Eval() can be used to send commands to the command line. Although disabling it will not protect you from all such vectors (an attacker could still utilize the system() or passthru() functions to bypass this protection). To enable this feature add the following to your php.ini file:

suhosin.executor.disable_eval = On

Another configuration option that can be of incredible use, but may be a little draconian is the option to disallow the uploading of binary files via PHP. Again, this will not protect against attackers uploading malicious scripts, but it would prevent an attacker from uploading, say, a compiled local kernel exploit. To enable this functionality use:

suhosin.upload.disallow_binary = On

Conclusions

Adding Suhosin to your PHP installation is a great way to dramatically reduce the exposure of your PHP based applications. Being aware of Suhosin is critical whether or not you implement it. Many developers utilize distributions with Suhosin compiled into PHP so they might not even realize the protections it affords, which can lead to disastrous results if they produce code expecting Suhosin protections in production which might not be applied. Understanding how Suhosin works and leveraging it to best suit your environment is a key step in hardening your PHP deployment. By utilizing Suhosin's logging only mode you can map out the influence a deployment will have on your environment, troubleshoot before moving to production, and gage the level of impact Suhosin will have. Given this feature there is very little down side to deploying Suhosin. Even if you don't deploy the more troublesome PHP patch, it is extremely quick, easy, and valuable to employ the Suhosin extension in your environment.