How to Install suPHP for PHP5 on CentOS 7
Posted on Updated onOn a shared server with multiple virtual hosts, we need to think about how to keep the files and scripts separate from one another. By default, PHP files are executed as “apache” or “nobody” – these are the default users that Apache creates. This creates security issues since PHP files need wide permissions and it leaves you vulnerable to code injection, and other alterations of PHP files from other users on the same server.
When we first install PHP on Linux, by default we see the following when we run “phpinfo()”:
Note that the “Server API” has the value “Apache 2.0 Handler”. What this means is that PHP is not running as a separate process for each script. Rather, it runs as part of Apache itself. This is usually an extremely efficient way of doing this. But as we see below, it might not be the most secure.
As an example, take the following PHP script:
<?php echo 'PHP running as: ' . exec('whoami'); echo '<br>Current script owner: ' . get_current_user(); ?>
When I place this file in my document root and run it, I get the following output:
As you can see, PHP is running as “apache” (on other systems, it might be “nobody”). Ideally, we would want every script to be executed by its owner. In this case, root. What we want is a PHP process where the two lines above show the same username.
Installing suPHP Instead of the Apache Handler
To correct this, we use a module known as “suPHP”. Despite development on suPHP stopping a while ago, where it only supported lower versions of Apache, it still remains popular. Luckily, the community continues to issue some patches, and a user named Aki Tuomi helped with issuing a patch for suPHP that allows it to work with Apache. Here’s how to install it on CentOS 7.
Installing Pre-Requisites:
Since suPHP is no longer in active development, it’s not directly available in official repositories. As a result, we need to compile it from scratch. To do this, we have to install some development tools using the following three commands:
yum -y groupinstall 'Development Tools' yum -y install apr-devel yum -y install httpd-devel
Without the last two commands, you’ll get an error during compilation that says:
checking for APR... configure: error: the --with-apr parameter is incorrect. It must specify an install prefix, a build directory, or an apr-config file.
Once the development tools are installed, it’s time to download the source code, patch it, and compile it.
Download, Patch, and Compile:
First, we download and extract the latest suPHP into a temp folder:
mkdir temp cd temp wget http://suphp.org/download/suphp-0.7.2.tar.gz tar zxvf suphp-0.7.2.tar.gz
Now we download and install the patch:
wget -O patchingsuphp.patch https://webhostinghero.org/downloads/php/suphp.patch patch -Np1 -d suphp-0.7.2 < patchingsuphp.patch cd suphp-0.7.2 autoreconf -if
Now we run the “configure” script in the folder like this:
./configure --prefix=/usr/ --sysconfdir=/etc/ --with-apr=/usr/bin/apr-1-config --with-apache-user=apache --with-setid-mode=owner --with-logfile=/var/log/httpd/suphp_log
And when that completes, we compile:
make make install
This compiles and installs suPHP on CentOS 7. But we’re not done by a long shot. First, we configure it to work with our installation:
Configuring suPHP:
Apache has a special folder called /etc/httpd/conf.d/ which contains a whole bunch of modules in the form of configuration files. We need to create one for suPHP as well.
Create the following file using an editor like vi:
vi /etc/httpd/conf.d/suphp.conf
And put the following line into it:
LoadModule suphp_module modules/mod_suphp.so
The above line tells Apache to include suPHP as a module. And now we need to configure suPHP itself. Create a file using a file editor like this:
vi /etc/suphp.conf
And paste the following inside:
[global] ;Path to logfile logfile=/var/log/httpd/suphp.log ;Loglevel loglevel=info ;User Apache is running as webserver_user=apache ;Path all scripts have to be in docroot=/ ;Path to chroot() to before executing script ;chroot=/mychroot ; Security options allow_file_group_writeable=true allow_file_others_writeable=false allow_directory_group_writeable=true allow_directory_others_writeable=false ;Check wheter 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=0077 ; Minimum UID min_uid=100 ; Minimum GID min_gid=100 [handlers] ;Handler for php-scripts x-httpd-suphp="php:/usr/bin/php-cgi" ;Handler for CGI-scripts x-suphp-cgi="execute:!self"
This takes care of suPHP configuration. But we’re not done yet! For suPHP to work, we need to create virtual hosts. Merely restarting Apache now won’t magically change the Apache handler to suPHP. For that to work, we have to set up our domains inside the document root directory.
Creating Virtual Hosts
This step is necessary for all the above to work. First, find out what your current document root is by executing the following command:
grep -i 'DocumentRoot' /etc/httpd/conf/httpd.conf
In my example, the document roots is /var/www/html . So I’ll be using that in my examples. Make sure you change it whatever shows up for you.
Create folder and Owner:
Next, we need to create a separate folder for our domain name (or names) in the document root. In my case, I’m just using an IP address, so I create a folder. My directory structure is now like this:
/var/www/html/104.236.92.101
Replace the part in bold with your own domain name or IP address. Next, we need to create an owner of the domain folder. Either create a new user, or you can assign an existing owner like this:
chown -Rf [user]:[user] /var/www/html/[domainname]
Note that [domainname] includes the “.com” or “.net” TLD. As before, replace [user] with an existing or new username and [domainname] with the name of the directory you just created. After this, whenever PHP creates files in [domainname] they will belong to [user].
Creating the Virtual Hosts file:
Just like adding a new module to Apache in /etc/httpd/conf.d/, we create a separate virtual hosts configuration file for each directory associated with a domain name. The name of the file, is the [folder/domainname].conf . Using a text editor, create a new file like this:
vi /etc/httpd/conf.d/[dommainname].conf
And paste the following into it:
<VirtualHost *> DocumentRoot /var/www/html/[domainname] ServerName [domainname] ServerAdmin webmaster@[domainname] <FilesMatch ".+\.ph(p[345]?|t|tml)$"> SetHandler None </FilesMatch> <IfModule mod_suphp.c> suPHP_Engine on <FilesMatch "\.php[345]?$"> SetHandler x-httpd-suphp </FilesMatch> suPHP_AddHandler x-httpd-suphp </IfModule> </VirtualHost>
This is the final piece of the configuration puzzle. Now just restart Apache using:
apachectl restart
or
systemctl restart httpd.service
And we’re done!
To test this, create a file in the new [domainname] directory under the document root and make sure that it’s owned by the group [user] and the owner is also [user]. To directly manipulate files on a remote server using a nice GUI interface, I recommend WinSCP. You can read my earlier tutorial on how to setup WinSCP securely on Linux. In the screenshot below, you can see my new file has both the group owner and user owner as “bhagwad:
Note how the permissions of the file are 0644. This is exactly in line with what is required by suPHP. In this new file, run phpinfo using the following code:
<?php phpinfo(); ?>
This will now give you the output as shown here:
Note how the “Server API” value has changed from “Apache 2.0 Handler” to “CGI/FastCGI”. This is the test you will use to know if your suPHP installation is working. If it remains “Apache 2.0 Handler”, it means something has gone wrong.
And also when I place the very first PHP file we ran and assign it the proper permissions, this is the output:
As you can see, PHP is now running as “bhagwad”, which is the same as the script owner. In fact, if you now try and run a script with a different owner, you’ll get a 500 permissions error. Your PHP process in a shared hosting environment is now secure!
Bhagwad, thank you for this tutorial. First, I am running an older Centos server and suPHP with a slightly different implementation (I believe with paranoid mode where suPHP_UserGroup is required.
It’s Centos 6.9 with Apache 2.2. I cannot recall where I got the suPHP from at this moment, but it was rpm’s and pretty simple to set up. However, that DID NOT work on
a Centos 7.3 running Apache 2.4.6.
So I followed your instructions and mostly everything seems to be working except – I have a “site builder” tool – and as soon as I load it, the permissions of two files it is using and writing to, get changed from 0640 to 0600. Then, the site builder just does not work properly, but there are no error messages in log files.
I cannot see that it is the site builder script that is changing the permissions of the files – it does not do that on other servers with exactly the same implementation. It is only doing it on the Centos 7.3 server, running suPHP in the way your tutorial above teaches. I have followed the directions exactly and am scratching my head here… have never seen file permissions just change like that!
I have put in a support request to the site builder developers just in case there is something going on there, but thought I’d also check with you to see if there might be something overlooked.
But otherwise, thank you so much for this tutorial!
I should also point out that of course, I did not follow EXACTLY – obviously your document root path is different, but that is the only difference here. :)
Thanks for writing this up, it was extremely helpful.
In the past for suphp I’ve used the following lines in my VirtualHost:
suPHP_UserGroup myuser mygroup
AddHandler x-httpd-php .php .php3 .php4 .php5
suPHP_AddHandler x-httpd-php
Yours basically looks like this:
SetHandler x-httpd-suphp
suPHP_AddHandler x-httpd-suphp
1) suPHP_UserGroup seems to no longer be used. If I try to add it to my VirtualHost config, it causes errors. Any idea why?
2) The “x-httpd-php” seems to have changed to “x-httpd-suphp”. I tried modifying back to “x-httpd-php” in both places in your VirtualHost code and I get errors. I figured this was a generic variable because it seems to be set in the SetHandler and then used in the AddHandler, so I thought it could be anything as long as the two are the same. Apparently not. Where is “x-httpd-suphp” defined?
Thanks again.
Terry