Using suPHP with Plesk

I wrote a while back about how we use ModSecurity as part of our standard server configuration. It has done a wonderful job in the past few years keeping all sorts of nasties away from our systems, but another layer of defense is never a bad thing right? We recently started using suPHP to add yet another level of security to our sites. suPHP is an Apache module "for executing PHP scripts with the permissions of their owners." It forces end users to run all php scripts with the proper (user configurable) permissions as well as keeping the script from executing as any other user except the owner of the file. This has some very distinct security advantages in that an end-user can be configured to have less access than the standard apache (or nobody) user/group.

Below is a typical file you might find on any Linux-based webserver:

-rw-r--r--  1 mcritch psacln       980 Dec 18  2010 index.php

This file has read/write permissions for the owner (mcritch), read only permission for the group (psacln), and read only permissions for everyone else.

Most web-based programs/CMS's require that certain files be writable in order to function. When a site has a file that needs to be written to it is often the case that these files either have to have group ownership of apache and/or have it's permissions set (chmod 775/777). In the example above, the file is only writable by the user mcritch. Since most webservers have a user named apache (or nobody) that the apache server software runs under, and because the apache process has to write/execute files owned by a wide number of paying customers, the permissions have to be loose. Apache would not be able to write to the file listed above without either changing them to:

-rw-rw-rw-  1 mcritch psacln       980 Dec 18  2010 index.php

OR

-rw-r--r--  1 apache:apache       980 Dec 18  2010 index.php

or something similar to this. All of these settings will cause problems and are not very secure.

If an attacker can figure a way to force the apache process to misbehave (perhaps due to some bad code in a single user's website) then the attacker can potentially overwrite files (among other things) in another user's directory. suPHP closes this hole in that it forces any file requested on a webserver to be executed by a process belonging to the owner of the file. This would allow the file they way it was first listed to be writable because the apache process will be running as "mcritch" for this file request. So, if an attacker is able to exploit bad code in a users website, only files owned by that particular user (mcritch in this case) are able to be overwritten. This prevents the attacker from accessing any other files, thereby containing the damage.

suPHP also fixes another problem that exists if you use Plesk. In Plesk, all files are owned by individual users AND by the group psacln. Plesk will pretty much happily execute any file as long as it either has this as the group OR has apache as the user and/or group owner. Files on a Plesk system often have this mixed up ownership because when end-users upload files into their site via FTP, they are owned by USERNAME:psacln. But, when they use a web-based app to do it (like Drupal, ModX, Wordpress, etc...) the files end up being owned by apache:apache. Because of this mish-mash of ownership, users are often forced to set permissions on many files and directories to be group/world writable (775/777) for the site code to function. suPHP fixes these because it forces all files uploaded via a web-based app to retain the same USERNAME:psacln ownership that it would get if uploaded via FTP, and eliminates the need for any files to have 664/666/775/777 as a permission.

Installing suPHP on a Plesk server is pretty straightforward. If you are using the Atomic yum repositories (we use just the atomic repo and NOT the plesk or bleeding/testing repos) you can type

yum install mod_suphp

Otherwise, you'll have to follow the instructions at www.suphp.org. Once you get it installed, you'll have to modify a few files on the system to get it going. Note that it is an apache module - it doesn't do anything unless you tell it to and installing it alone does not make it function!! You'll have to make some site-wide AND per-domain changes.

First, modify the file /etc/suphp.conf. I've marked the areas you probably need to look at with a "you may need to modify this" flag.

[global]
;Path to logfile
logfile=/var/log/suphp.log
;Loglevel
loglevel=warn 
;User Apache is running as - you may need to modify this
webserver_user=apache 
;Path all scripts have to be in - you may need to modify this
docroot=/var/www/vhosts/
; Security options
allow_file_group_writeable=false
allow_file_others_writeable=false
allow_directory_group_writeable=false
allow_directory_others_writeable=false 
;Check whether script is within DOCUMENT_ROOT
check_vhost_docroot=true
;Send minor error messages to browser
errors_to_browser=false
;PATH environment variable
env_path=/bin:/usr/bin
;Umask to set, specify in octal notation
umask=0022
; Minimum UID - you may need to modify this
min_uid=500
; Minimum GID - you may need to modify this
min_gid=500
[handlers]
;Handler for php-scripts
x-httpd-php="php:/usr/bin/php-cgi"
;Handler for CGI-scripts
x-suphp-cgi="execute:!self"

Next, you'll need to modify /etc/httpd/conf.d/mod_suphp.conf

LoadModule suphp_module modules/mod_suphp.so 
suPHP_AddHandler x-httpd-php
AddHandler x-httpd-php .php
# This option tells mod_suphp which path to pass on to the PHP-interpreter
# (by setting the PHPRC environment variable).
# Do *NOT* refer to a file but to the directory the file resists in.
# E.g.: If you want to use "/path/to/server/config/php.ini", use "suPHP_Config
# /path/to/server/config".
#
# If you don't use this option, PHP will use its compiled in default path.
suPHP_ConfigPath /etc 

In order to activate this module on a Plesk domain, you'll first need to modify the ownership and permissions of the files in the website. Note that since this is an apache module, you'll need to configure it to run on each domain hosted on your server. I've provided some shell commands to speed up the process. They will change all files to 644 and directories to 755 (remember directories need to be executable for apache to "see" them). Login as root and do the following (per domain)

cd /var/www/vhosts/DOMAIN.COM
chown -R USERNAME:psacln httpdocs

cd /var/www/vhosts/DOMAIN.COM/httpdocs
find . -type d -print0 |xargs -0 chmod 755
find . -type f -print0 |xargs -0 chmod 644

You'll also have to create/modify the vhost.conf for that domain. Edit (or create if it doesn't exist) the file /var/www/vhosts/DOMAIN.COM/vhost.conf (if you have a SSL site you will also need to edit (/var/www/vhosts/DOMAIN.COM/vhost_ssl.conf) and add the following: (again, per domain)

<IfModule mod_suphp.c>
<Directory "/var/www/vhosts/DOMAIN.COM/httpdocs">
php_admin_flag engine on
suPHP_Engine On
suPHP_ConfigPath "/var/www/vhosts/DOMAIN.COM/httpdocs" 
AddHandler x-httpd-php .php
AddHandler php5-script .php
AddHandler x-httpd-php .php .php5 .php4 .php3 .phtml
suPHP_AddHandler x-httpd-php
suPHP_AddHandler php5-script .php
php_value open_basedir "/tmp/"
php_value upload_tmp_dir "/var/www/vhosts/DOMAIN.COM/tmp/"
<Files php.ini>
order allow,deny
deny from all
php_value open_basedir "/tmp/"
php_value upload_tmp_dir "/var/www/vhosts/DOMAIN.COM/httpdocs/tmp/"
</Files>
</Directory>
</IfModule>

Note that /var/www/vhosts/DOMAIN.COM is the default path in plesk. You may need to change these to fit your installation. After you've modified (or created) the vhost.conf file, you'll need to tell Plesk it's there by doing

/usr/local/psa/admin/sbin/websrvmng -u --vhost-name=DOMAIN.COM

and then restarting apache.

One problem that WILL occur however when you complete this whole transition - any PHP sessions that were previously created will be owned by apache. This will cause fits for anyone who hasn't flushed their browser cache because the browser will try to pick up where it left off (as most default PHP configurations keep sessions active until the browser is closed). Also, the default location for session files (/var/lib/php/session) is only writable by apache - now that the scripts are running as the owner of the files the session files can no longer be written to this directory. You will either have to change the permissions on this folder to 775 or change it (modify session.save_path in your /etc/php.ini) to a directory that is writable by everyone. If I did the latter I would add a sticky-bit (chmod +t directoryname) so only the owner can modify their own files, or just use /tmp (which already has a sticky-bit by default). I'm in favor of using /tmp because when you upgrade php it will reset the permissions on the default directory back to 750. You'll probably want to delete the old session files or you'll have problems with people not being able to load the website. After we enabled suPHP and cleaned up our sessions things worked just like they were supposed to. Combine this with ModSecurity's scrubbing out of all suspicious posts/requests, and not only are your sites functioning they way they are supposed to, they end up being much more secure to boot!