Last updated 3 September 2015. Created on 1 September 2015.
Edited by webel. Log in to edit this page.

Some context

So why might you, if you are operating a cPanel-based VPS for your own Drupal sites, want to consider switching from suPHP to DSO ?

"Your Drupal site got hacked ? Well it's entirely your own fault ! You should always do your updates ! You've only got yourself to blame (and don't dare blame the security holes in that older Drupal code) !!!"

What, every single day ? Update 20 sites across 7 servers (even with Drush) ? Have you ever been on holiday (without your spouse leaving you in frustration because you spent your entire time doing Drupal updates) ? Or what about sick, ever been to hospital for a week or two ? Besides, how long did it take for Drupa(l)geddon to strike ? Quicker than Ebola it was ! I need a way please to get "less hacked", and suffer less (when I for example don't do my updates and have security notifications off).


Now back in the real world, if you have a "typical" cPanel-based VPS or Dedicated Server with CentOS and Apache you mostly have a choice between these two PHP handlers with the following fundamental security implications:

- Using suPHP - which runs the web server as your cPanel user - and if your Drupal CMS gets hacked other "customers" (in case you have any) on the same server are protected, but the hacker can run completely riot across your entire cPanel user filesystem area.

- Using older (and mostly a bit faster) Dynamic Shared Object (DSO) (a.k.a. mod_php) - which runs as 'nobody' - and if your Drupal site gets hacked other "customers" (in case you have any) on the same server (that you control and manage) are less protected against anything 'nobody' can do, but as 'nobody', the hacker can do much less damage to your user filesystem portion, especially if setup specifically for the DSO "web-server as nobody" configuration.

Mmmm, I wonder, if you are in fact completely alone on a VPS with multiple Drupal sites (of your own making) under one cPanel user account, which one to choose, suPHP (the one the customer-friendly VPS server hosting companies typically offer by default) or DSO ? Do you sneakily, or even a bit "selfishly" (but not too horribly, since you are completely alone on the server anyway) choose DSO ?

You read the WHM/cPanel guide to PHP and suEXEC configuration, you make a complete cPanel backup (and download it somewhere safely) and then you take the big step, you choose that "older" DSO PHP handler option (instead of suPHP), and you bravely push the WHM apply button (which does not in fact overwrite any customisations you made to /usr/local/lib/php.ini, although the WHM GUI makes it look like it would).

And you know you will get there. Your Drupal web sites that were running fine under suPHP before you pushed that DSO button on WHM may not be working for the moment, but very soon they will again, if you just carefully follow this guide step-by-step. Well, almost certainly.


IMPORTANT: the following assumes throughout that all of your Drupal module maintenance installs and all core and module updates will be performed using Drush terminal commands (likely via SSH to the server) as the cPanel user (not as 'root'), although you may still need 'root' for some rare situations.

Also, please do not (if following the recipe here) assign pseudo-user 'nobody' to a new, extra _www or apache or web server group or such, it undermines the meaning/convention of 'nobody'. The following guidelines are designed to preserve the 'nobody' concept when it is known that the web server is running as 'nobody'.

CAVEAT: speaking of 'nobody', nobody is every going to guarantee that any web site platform permissions recipe will 100% stop hackers, and this recipe is no different. And do please report flaws or challenge the recipe appropriately if you feel you've spotted any problems.


POSSIBLE GOTCHA: some of the stricter ("ultra-paranoid") removals of all 'r' readership permission from any PHP scripts that are not immediately in use (as recommended below) - and from informational Drupal text files- may cause problems with WHM/cPanel backups (if performed as the cPanel user). In such cases, simply add u+r (but no more in order to stay "quite paranoid"), to those PHP scripts not immediately in use. This typically includes: install.php, update.php, default.settings.php, web.config.


Check DSO and web server 'nobody' configuration

Perform this check once via a browser to confirm the web server is running as 'nobody' and then (assuming it is) disable it completely or move it:

# cat > whoami.php 
<?php echo shell_exec('whoami');

Load your site: http://www.example.com/whoami.php:

nobody

If you are using suPHP still you should instead get your cPanel user name.

Now disable it (or move it elsewhere):

# chmod og-rwx,u-wx+r whoami.php

Check also BTW that a phpinfo test script gives this (to prove it is running DSO not suPHP) then move or disable it:

# cat > phpinfo.php 
<?php phpinfo(); ?>

Load your site: http://www.example.com/phpinfo.php and check for the Server API setting, if it is in DSO mode it will say:

Server API: Apache 2.0 Handler

Now disable it (or move it elsewhere):

# chmod og-rwx,u-wx+r phpinfo.php

Only enable phpinfo.php temporarily as needed, as it reveals quite a lot about your server's setup (or just always call it via command line as php -i or php -f phpinfo.php).


Configure DSO and Drush-friendly ownership and permissions for the top-level Drupal install

From your respective Drupal web root .../public_html (or maybe say .../public_html/drupal7):

- First, in case you are not sure about the state of the ownerships and permission (because you say recently imported it from a different system, or because you have used Drush both as the cPanel user and as WHM 'root') set the entire system into a known state:

# chown -R [username]:[username] *

Substitute your cPanel username for [username].

- VERY IMPORTANT: you MUST have the group of of the web root (your dot .) as 'nobody' !

# chgrp nobody .

- Completely disable any writes by others (both non-user and non-group) recursively across the entire system (assuming you only have Drupal sites on this part of the machine of course):

# chmod -R o-w

- At the respective Drupal root protect your custom php.ini (in case you have one):

# chgrp nobody php.ini
# chmod a-x,o-rw,g-w+r,u+wr php.ini

This should be preserved (not overwritten by) Drush.

- The following Drupal files at the Drupal root could also be made group 'nobody' but might then get clonked by Drush, so we'll leave them with the cPanel user, but they do need to be readable by the web server as 'nobody':

# chmod a+r-wx index.php cron.php xmlrpc.php authorize.php
# chmod a+r-wx .htaccess

The authorise.php has a special role with access from Drupal controlled by switches in each site's settings.php file, visit authorize.php.

Beware that - unless you are using Git tricks or such - Drush core updates will overwrite any customisations you have made to .htaccess, so always keep a copy somewhere before you 'drush up' core. Same goes for the robots.txt

Since installs and updates are to be performed (according to the recipe here) only with Drush via command line, and never via a web browser, disable these completely:

# chmod og-rwx,u-wx+r update.php install.php

(They are left user readable so WHM/cPanel backups made as user will not complain and miss them.)

So your php script files should now look like this:

-r--r--r-- myuser myuser authorize.php
-r--r--r-- myuser myuser cron.php
-r--r--r-- myuser myuser index.php
-r-------- myuser myuser install.php
-r-------- myuser myuser update.php
-r--r--r-- myuser myuser xmlrpc.php

And your .htaaccess like this:

-r--r--r-- myuser myuser .htaccess

Note if you want to permit writing of say blacklisted IPs by the cPanel as user to the .htaccess you'll have to open it up to be user writeable:

# chmod u+w .htaccess 
-rw-r--r-- myuser myuser .htaccess

CAVEAT: if you look at the authoritative Securing file permissions and ownership community docs you'll see that it's setup with a web server group www-data and the files index.php, authorize.php, and .htaccess all NOT world/other readable but readable by both the user and the group www-data, and writeable by the user. By contrast, In the recipe above, they are world readable and thus readable by the web server as 'nobody'. Note also that (although these files rarely change) they could potentially have permission and ownership changed by Drush updates. See also this external link from Randy Fay Drush, File Permissions, Web Servers, and the coming Armageddon.

If you used the extra diagnostic scripts (explained at the top) they should look like this (or move them out of the web root completely):

-r-------- myuser nobody phpinfo.php
-r-------- myuser nobody whoami.php

- Disable reading of any Drupal text files as 'nobody' (as "other" in this case) ! Some of these text files like CHANGELOG.txt in particular scream out loud what Drupal version you are using and list past vulnerabilities (which may imply what has NOT yet been covered by your current running Drupal version). But do ensure robots.txt is readable:

# chmod a-wx,og-r,u+r *.txt
# chmod a+r robots.txt

(They are left user readable so WHM/cPanel backups made as user will not complain and miss them.)

So your text files should now look like this:

-r-------- myuser myuser CHANGELOG.txt
-r-------- myuser myuser COPYRIGHT.txt
-r-------- myuser myuser INSTALL.mysql.txt
-r-------- myuser myuser INSTALL.pgsql.txt
-r-------- myuser myuser INSTALL.sqlite.txt
-r-------- myuser myuser INSTALL.txt
-r-------- myuser myuser LICENSE.txt
-r-------- myuser myuser MAINTAINERS.txt
-r-------- myuser myuser README.txt
-r--r--r-- myuser myuser robots.txt
-r-------- myuser myuser UPGRADE.txt

- Since this is about CentOS (Linux) and Apache, you don't need the Drupal7/8 web.config file for Windows ISS at all, so disable it (noting that a Drush core update will only restore it anyway it if you delete it):

# chmod a-wx,og-r,u+r web.config

While you are there, check for any non-Drupal auxiliary XML and HTML files used by search engines etc. such as BingSiteAuth.xml,LiveSearchSiteAuth.xml, Google HTML key files etc. They do not need to be writeable by anybody:

# chmod a+r-wx *.xml *.html *.htm

Check also that there aren't any legacy index.html "greeting" files hanging around from your loving web host, if there are any then disable them completely (or better, move them away out of your web root forever, which makes it easier to spot if a hacker has introduced unwanted HTML hack files that have nothing to seek on a purist Drupal system).

And now it is finally time to secure your /sites

From .../public_html/sites be completely (yet consistently) paranoid and close these off:

# chmod a-rwx,u+r README.txt example.sites.php

If you have a sites.php use:

# chgrp nobody sites.php
# chmod a-wx,ug+r sites.php

You could do the next step also from .../public_html/sites and apply it across every site with wildcards, but it is probably safer to do it from within each site's folder, just in case there are special circumstances.

We will assume you are in .../public_html/sites/default.

The settings.php file need not remain writeable by the user the entire time, and won't get clonked by Drush, so once you are happy with it (and you have set the $base_urlto pass the Security Review module's test) you can safely set it to be in the 'nobody' group and restrict it thus:

# chgrp nobody settings.php
# chmod a-wx,ug+r,o-r  settings.php

If you have a default.settings.php hanging around close it off:

# chgrp nobody default.settings.php
# chmod a-rwx,u+r default.settings.php

Both the themes and modules (in case you have any outside all) have to be writeable ONLY by the cPanel user, which you'll be using to execute Drush update commands, but they must remain completely with the user, yet readable by 'nobody', so:

# chmod -R a-x,u+rwX,og+rX themes modules

And now to the files, which must be writeable by the web server as 'nobody'. Don't leave files executable for no reason, only the directories/folders need it (hence recursively removing with -x then adding with -X ):

# chgrp -R nobody files
# chmod -R a-x,o-rw,ug+rwX files

When files are uploaded by the web server they will be both in the 'nobody' owner and group:

-rw-rw-r-- nobody nobody uploaded.png

But some older files (unless you did the initial recursive username:username ownership and group sweep recommended at that top) may be still username:nobody. This doesn't usually matter much; you won't be able to mv or chmod uploaded files as the cPanel user, which is one slight disadvantage compared with the suPHP "web server as user" approach, but the security benefits outweigh this slightly inconvenience. This recipe is for Drush and terminal users and it is assumed you have 'root' access anyway if needed (and under normal CMS operation, you probably won't often have to manipulate uploaded files).

After all of this it should look something like this:

drwxr-xr-x  myuser myuser   ./
drwxr-xr-x  myuser myuser  ../
drwxrwx---  myuser nobody  files/
drwxr-xr-x  myuser myuser  modules/
-r--r-----  myuser nobody  settings.php
drwxr-xr-x  myuser myuser  themes/

And finally, in your .../public_html/sites/all:

# chmod -R a-x,u+rwX,og+rX themes modules libraries

Now test your sites. Test that they load, that all images and CSS and JS resources load, and that you can upload images and files etc., and see in particular what happens in the .../files folder. Make sure also to run a Security Review check, and if you are using Drupal7/8 (not Drupal6), perhaps also a Site Audit.

Concluding remarks

This may seem at first like a bit of of extra work compared with suPHP, but I feel it has a very important advantage; it forces you to think about what the user is doing and what the web server is doing, and once it is setup properly it preserves that separation of duties forever, even robustly against Drush (when used strictly as the cPanel user). By contrast, using suPHP, for all of its "web server as user" convenience, might just encourage you to be a bit lazy; Certainly, it makes it much harder to see what the user has done , what the web server has done, and maybe even what a hacker as done "as user" !

Looking for support? Visit the Drupal.org forums, or join #drupal-support in IRC.