Much to my disappointment PHP installs with developer settings by default. This means that the default installation has some potential security holes that need to be tightened after the install.
Any security person will tell you that an application should install in a minimal and locked down state. I would say 95% of people don’t look to secure something after they install it, they expect it to be secure out of the box. This is what IIS now does and you have to enable any features over the baseline you want.
Doing some simple lockdowns are not hard in PHP though. I am not going to talk about every security feature in the config file, just the defaults that I feel should be changed:
safe_mode=On [Default=Off]
Turning this on makes it so that any file your php file attempts to open MUST have the same UID as the php file that is executing. It also disables or restricts many methods that access or manipulate the filesystem.
safe_mode_gid=Off [Default=Off]
Same as safe_mode but checks the GID instead of the UID. I keep this setting at its default Off state as I want to keep the access restricted to only a user.
open_basedir=[path] [Default=blank]
Restricts PHP to only access files and folders within that path. If all your sites exist in /usr/home/web then I would set open_basedir=/usr/home/web
expose_php=Off [Default=On]
PHP will not write information about itself to HTTP headers. This can help prevent a vulnerable version from being discovered by automatic scripts.
display_errors=Off [Default=On]
By turning this off error messages are not returned to the browser. This prevents information disclosure which is often a precursor to an attack.
log_errors=On [Default=Off]
turning this on logs all the errors to a file or syslog (as specified in the error_log variable)
disabled_functions=show_source, system, shell_exec, passthru, exec, phpinfo, popen, proc_open [Default=Blank]
This allows you to completely disable certain functions in the PHP runtime that could be potentially used to exploit your system.
enable_dl=Off [Default=On]
Disables the loading of a PHP extension at runtime (i.e. dl('myexploit.so');).
allow_url_fopen=Off [Default=On]
Disables using fopen to open a URL. When left on it is possible it could be exploited to download exploit code from a remote server.
upload_tmp_dir=/var/tmp/php/tmp [Default=/var/tmp]
I change this so that automated attacks can not find any uploaded files in the default directory. It also enables you to add customized security around the directory that is restricted to its purpose.
session.save_path=/var/tmp/php/sessions [Default=/var/tmp]
Whenever a session is created it is saved to the disk. This could potentially expose the session IDs of all current sessions if an attacker can enumerate them. Changing the folder that the session files are saved to can reduce this risk.
There are also a few limits that you may want to adjust to suit your environment. I find the default memory limit quite high (128MB) but this will differ from environment to environment:
max_execution_time = 30 ; Maximum execution time of each script, in seconds
max_input_time = 60 ; Maximum amount of time each script may spend parsing request data
memory_limit = 128M ; Maximum amount of memory a script may consume (128MB)
Using Apache to provide additional security
Many php systems ship with files that are used by the application but should never be served out to the application. The common ones are .inc, .sql, and .conf (or .config) files. Also some editors create temp files that end in a ~ and may not get cleaned up properly. We can change our Apache config to deny these files from ever being served:
<FilesMatch "\.(inc|sql|config|conf |.*~)$">
Order allow,deny
Deny from all
</FilesMatch>
One of the issues is that with safe_mode set to on an attacker could still write to all files that are owned by that user. Lets take the following example
/usr/home/web/user1/index.php Owner: nobody
/usr/home/web/user2/index.php Owner: nobody
/usr/home/web/user3/attack.php Owner: nobody
Even with safe_mode=On and open_basedir=/usr/home/web, running attack.php could alter index.php in user1 or user2’s directory. I know it is not common to have this kind of scenario where the same user owns all the files but it is quite possible.
I saw a nice workaround somewhere that used Apache to set the open_basedir for each virtual host that looked something like this:
<VirtualHost www.user1.com:80>
<Location />
php_admin_value open_basedir "/usr/home/web/user1"
</Location>
</VirtualHost>
This would basically lock every user into their own home directory which is exactly what you would want in a shared environment.
Suhosin
Suhosin is a two part hardening add-on to PHP. One part patches PHP to help prevent buffer overflows or format string vulnerabilities. The second part is a PHP extension that adds a host of other protections and features. More info on Suhosin can be found on their website.