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 >, <, " 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.