Updated: 18th May 2012

PHP Handlers & File Permissions.

Account User

In a typical commercial Linux web hosting setup with WHM/cPanel you will have a user account created with your website. This is the account you will use to transfer files via FTP/SFTP and SSH into the server. In any case you will always need some kind of user account to transfer files onto the server and manage them. From here on when I refer to the account user or the account user:group I am referring to whatever user:group you upload/manage the website files with. You can belong to multiple groups but this in not typical in most commercial web hosting setups. In any case when creating files the files are owned by your primary group. So when I mention user:group I really mean user:primarygroup

Apache User

The Apache web server will typically run as apache:apache, nobody:nobody or www-data:www-data. It varies from system to system but essentially it is a user with limited permissions created specifically for Apache. Apache processes will have the permissions of this user when accessing the file system. From here on when I refer to the Apache user or Apace user:group I am referring to whatever user:group Apache is running as. Typically this user will only belong to a single primary group and this is the group I am referring to.

PHP User

When PHP scripts are executing they do so as a certain user and group. They are limited by the permissions given to this user on the file system. Sometimes it is the same user as the Apache user:group but at other times it is the same user as the account user:group. As you will see it is important to know which user:group your PHP scripts are running as. From now on when I refer to the PHP user or PHP user:group I am referring to whatever user:group PHP is running as. Once again typically this user will only belong to a single primary group and this is the group I am referring to.

PHP Handlers

PHP handlers are how the Apache web server handles requests to PHP files. Apache can be configured to serve PHP files in a number of different ways. Each way has it's advantages and disadvantages. Primarily I am focusing on the affect to security, file permissions and PHP settings configuration. Here is a summary of the most common PHP handlers, pay close attention to what user the PHP scripts run as in each summary...

DSO (Dynamic Shared Object)

  • third party apache module called mod_php
  • php built directly into apache using this module
  • php scripts run as Apache user:group
  • can configure PHP settings with .htaccess
  • very fast, low CPU usage

CGI (Common Gateway Interface)

  • apache module called mod_cgi or mod_cgid
  • runs PHP scripts (and other types of scripts) as external programs
  • new process created for each request of script
  • scripts run as Apache user:group
  • can enable suEXEC to run scripts under the account user:group
  • since PHP 5.3.0 can configure PHP settings with INI files on a per-directory basis
  • older versions of PHP used the PECL htscanner extension to achieve the same thing

FastCGI

  • apache module called mod_fcgid
  • runs PHP scripts (and other types of scripts) as external programs
  • high performance alternative to CGI
  • scripts run as Apache user:group
  • can enable suEXEC to run scripts under the account user:group
  • since PHP 5.3.0 can configure PHP settings with INI files on a per-directory basis, older versions of PHP used the PECL htscanner extension to achieve the same thing
  • fast, low CPU usage
  • high memory usage

suPHP (Single user PHP)

  • third party apache module called mod_suphp
  • requires CGI
  • php scripts run as the account user:group
  • high CPU usage

phpSuExec

  • outdated, similar to suPHP

WHM Options

With a WHM/cPanel setup you can configure the PHP handler being used as follows.

  • under Service Configuration -> Configure PHP and SUExec
  • can select PHP5 handler: none, cgi, dso, suphp
  • can turn Apache suEXEC on or off
  • suEXEC will only work for PHP if PHP handler is CGI, however you can set PHP handler to something else and still enable suEXEC to work for other types of CGI scripts such as perl

Which user:group are the PHP scripts running as?

As you have seen PHP will execute as different users depending on the server setup. It is important to know which user your PHP scripts are running as when setting file permissions. <?php echo exec('whoami'); ?>

Then to find the primary group <?php echo exec('id'); ?>, the gid is the primary group

Protecting Files

On a server with multiple accounts you need to protect the files in each account from being accessed from the other accounts. This is especially important on shared servers because the other accounts belong to strangers. On a dedicated server or VPS you at least know the other accounts belong to your clients/friends. However even in this case you should still protect the accounts from one another to limit the damage if one of the sites gets hacked. So you need to tighten up file permissions to protect your files from other accounts. You must also remember that other accounts on the server could have the ability to execute their own scripts as the Apache user:group.

However you can't tighten up permissions too much. You need to consider that your account user, the apache user and PHP user will all need varying levels of permissions on your website files for your website to operate properly.

Generally speaking

  • your account user will need read and write access to all website files so you can upload, delete and manage files.
  • the Apache user will need read access to all public website files so it can serve them when requested.
  • the PHP user will need read and write access to certain website files, which files and what permissions depends on the PHP scripts in your website.

File permissions for servers running PHP as the Apache user:group

When running PHP embedded in an apache module (mod_php) or as a CGI/FastCGI without suEXEC, PHP will execute as the Apache user:group (see above). This means whatever permissions your PHP scripts need has to be given to the Apache user:group. The obvious way would be to set global permissions on the files so the Apache user:group can access them but this is a potential security risk as you are opening them up to everyone. You can partly mitigate the problems by changing the group owner of the files PHP needs permission on to the Apache group and relaxing group permissions. This way you are only opening access up to the Apache group and not to other user accounts on the server. Combined with this if you set open_basedir (see below) you will stop attacks from PHP scripts in other accounts running as the Apache group. Of course even with all this there is nothing stopping a malicious perl/python/etc script in another account running as the Apache user:group and accessing these files. So it's actually impossible to totally secure files from other accounts when PHP runs as the Apache user:group. However you can improve things and make it harder by setting open_basedir and setting appropriate file permissions. Here are some file/folder types and recommended permission settings...

  • files should be owned by the account user:group unless otherwise specified.
  • we keep in mind both the account user and the Apache user (which PHP also runs as) when setting these permissions.
  • these are just some suggestions which haven't been fully tested, if having problems with website functionality you might need to loosen permissions.

/www/ - chgrp to the Apache users group (750)
   -web root
   -Apache needs access

/www/readme.txt - (600)
   -installation instructions for a cms system
   -Apache doesn't need access to a readme file, so tighten up

/www/css/ - chgrp to the Apache users group (750)
   -contains static css files
   -Apache needs access

/www/css/style.css - chgrp to the Apache users group (640)
   -static css file used in website design
   -Apache needs access

/www/uploads/images/ - chgrp to the Apache users group (770)
   -folder for user uploaded images
   -PHP needs read and write access, Apache needs access

/www/uploads/images/uploadedfile.jpg - (400)
   -file created by PHP and owned by Apache user:group
   -Apache needs read access
   -file should have enough permissions by default
   -if not you can chmod inside PHP script after creating file

/www/lib/config.php - chgrp to the Apache users group (640)
   -file contains database password etc
   -PHP needs read access

/www/lib/classes/mailer.php - chgrp to the Apache users group (640)
   -a utility class for sending out emails
   -PHP needs read access

/www/index.php - chgrp to the Apache users group (650)
   -bootstrap file accessible through web browser
   -Apache might need execute permission

/www/.htaccess - chgrp to the Apache users group (640)
   -Apache configuration file
   -Apache needs access

File permissions for servers running PHP as the account owner

When running PHP as suPHP or CGI/FastCGI with suEXEC, PHP will execute as the account user. Any permissions your account user has, PHP scripts will also have. This is good because it means you restrict file permissions aggressively. You still need to open up some read permissions for the Apache user:group as Apache needs direct access to public website files so it can serve the HTTP requests. However under this setup Apache will rarely if ever need any write permissions. Here are some file types and recommended permission settings...

  • files should be owned by the account user:group unless otherwise specified.
  • we keep in mind the account user, the Apache user and the PHP user when setting these permissions.
  • these are just some suggestions which haven't been fully tested, if having problems with website functionality you might need to loosen permissions

/www/ - chgrp to the Apache users group (750)
   -web root
   -Apache needs access

/www/readme.txt - (600)
   -installation instructions for a cms system
   -Apache doesn't need access to a readme file, so tighten up

/www/css/ - chgrp to the Apache users group (750)
   -contains static css files
   -Apache needs access

/www/css/style.css - chgrp to the Apache users group (640)
   -static css file used in website design
   -Apache needs access

/www/uploads/images/ - chgrp to the Apache users group (750)
   -folder for user uploaded images
   -PHP needs read and write access, Apache needs access

/www/uploads/images/uploadedfile.jpg - (444)
   -file created by PHP and owned by account user:group
   -Apache needs read access
   -file should have enough permissions by default
   -if not you can chmod inside PHP script after creating file

/www/lib/config.php - (600)
   -file contains database password etc
   -PHP needs read access

/www/lib/classes/mailer.php - (600)
   -a utility class for sending out emails
   -PHP needs read access

/www/index.php - chgrp to the Apache users group (750)
   -bootstrap file accessible through web browser
   -Apache might need execute permission

/www/.htaccess - chgrp to the Apache users group (640)
   -Apache configuration file
   -Apache needs access

No root access?

On a shared server you probably won't have root access. Without root access you won't be able to change the group owner as outlined above on certain files. In this case you will have to ask the server administrator/hosting company to change group ownership for you. Otherwise for those files where I say to chgrp to the Apache users group you will instead have to modify the permissions so other has the same rights as group.

So in the case of
/www/css/style.css - chgrp to the Apache users group (640)
You would have to chmod to 644 instead of 640.

Obviously this opens up a potential security problem but if you don't have root access to the server you have no choice.

FTP changing file permissions?

Be careful when overwriting existing files when using ftp. It might reset the file permissions. Either edit files directly on the server or fix permissions after ftp upload.

Safe mode/Open Basedir

safe_mode is PHPs internal way of handling the file permissions problem on servers with multiple accounts. When you try an access a file within PHP, safe_mode checks to see that the file you are opening is owned by the same user as the owner of the PHP file being executed. safe_mode has a number of drawbacks and is being removed in PHP6 so it's probably best to just disable safe_mode.

open_basedir is another way PHP tries to solve the file permissions problem on servers with multiple accounts. It simply limits PHP scripts from opening files outside a particular directory. So you can configure this per account on the server to restrict PHP accessing other peoples files. This can be useful when executing PHP scripts as the Apache user:group.

Stopping Apache serving files

Sometimes you want to stop certain files on a website being directly accessible using HTTP, you want to stop people directly browsing to the file in their web browser.

You could stop a web server directly serving certain files by setting up permissions so it can't read them. For example you might want to stop people directly browsing for /www/lib/config.php. However if you changed permissions to stop the web server reading this file then depending on setup the PHP script might also fail reading the file when trying to include it! So in this case it's easiest to use .htaccess to tell Apache not to serve this file directly. To do this create a .htaccess file inside /www/lib/ which has a single line: deny from all

This way the web server will refuse to serve the file to someone requesting it over HTTP but a php script such as /www/index.php can still have access to the file. You can also achieve the same thing by placing the whole lib folder outside of the web root. So if you had /www/lib/ you could move it to /lib. Then you would have /www and /lib sitting alongside each other. Of course you would need to modify your php include code to take into consideration to new location of your lib files.