FastCGI + suExec + PHP

By Paulus, 12 April, 2011

In order to increase security on a webserver, especially on a shared host, using suExec and FastCGI to execute a PHP script as a normal user will add a level of security. For example, we have a vhost configured already:

<VirtualHost *:80>
    ServerAdmin webmaster@dummy-host.example.com
    DocumentRoot /var/www/html/example
    ServerName example.com
    ServerAlias www.example.com
    ErrorLog logs/example.com-error_log
    CustomLog logs/example.com-access_log common
    <Directory /var/www/html/example>
       DirectoryIndex index.php
       AllowOverride All
       Allow from all
       Order allow,deny
    </Directory>
</VirtualHost>

 The first step is to make sure that the suExec and FastCGI module are enabled, in the httpd.conf or apache.conf (depending on which distribution you're using) for suExec and for FastCGI, look for a file called fcgid.conf located in the conf directory.

LoadModule suexec_module modules/mod_suexec.so

Now we need to go back to the vhost directive. Add the following lines that are in bold:

<VirtualHost *:80>
    ServerAdmin webmaster@dummy-host.example.com
    DocumentRoot /var/www/html/example
    ServerName example.com
    ServerAlias www.example.com
    ErrorLog logs/example.com-error_log
    CustomLog logs/example.com-access_log common
    SuexecUserGroup user group
    <Directory /var/www/html/example>
       DirectoryIndex index.php
       Options +ExecCGI
       AddHandler fcgid-script .php
       FCGIWrapper /var/www/conf/php-fcgi .php
       AllowOverride All
       Allow from all
       Order allow,deny
    </Directory>
</VirtualHost>

Where you place your document root is up to you and where you place your php-fcgi (or what you decide to name it) is also up to you. The next step is to make all php files executeable.

 For the php-fcgi script, which is located in /var/www/conf/ should contain the following:

#!/bin/bash
PHP_CGI=/usr/bin/php-cgi
PHP_FCGI_CHILDREN=4
PHP_FCGI_MAX_REQUESTS=1000

### no editing below ###
export PHP_FCGI_CHILDREN
export PHP_FCGI_MAX_REQUESTS
exec $PHP_CGI

the file should be 755

$ find . -type f -iname '*.php' -exec chmod u+x '{}' \;

suExec only handles binaries or shell scripts that are executable which is why we made all the php files executable in the above command. In order for a script to be executable, the script needs to have a #!interpreter before any code. One way to solve this problem is to add a #!/usr/bin/php-cli to the beginning of each php file or run the following command which is the equivalent of telling windows to 'always use this program when opening this kind of file.'

$ echo ':PHP:E::php::/usr/bin/php-cgi:' > /proc/sys/fs/binfmt_misc/register

PHP (1st position) - is used as the name.
E (2nd position) - is saying that this is going to be an executable file. The other option for this position is M, or magic, which assocates a binary file (eg: exe) with a program.
php (4th position) - The type of file we're associating.
/usr/bin/php-cgi (6th position) - What is to be run when this type of file is encountered.

If this still isn't working, and you may need to add the following line to the php.ini file:

cgi.force_redirect=0

Finally you can restart apache:

# service httpd restart

You can test this by creating a php file in the directory you specified in your VirtualHost directive.

<?php
echo system('id');
?>