The “traditional” web server just reads and sends out files in response to incoming requests. Consequently, the standard security configuration is therefore set up to give web accesses the bare minimum in terms of file permissions: the ability to read the site’s files, but not to change them.
But many PHP applications want to write files as well: forums that support uploading files, CMS applications, and many Wikis all create or update files as a normal part of their operation. Since the default permissions don’t allow it, many people run into trouble when trying to develop or install PHP applications that need this ability. This blog post will attempt to show how to do this on our system in a way that is easy to set up and very secure.
On Unix systems like ours, each file has two owners: a user and a group. On our system, the user who owns your files is usually identified to you as “me” and the web server runs as user “web.” Similarly, there are two groups: “me” and “web.” When you are logged in to the ssh server, your default group is group “me” but you are a member of both “me” and “web.” The web server, on the other hand, is limited to the “web” group.
PHP safe_mode imposes a number of restrictions above and beyond standard Unix filesystem permissions. For the most part, these restrictions are designed to prevent multiple sites on a shared host (like ours) from trampling each other. However, if applied properly, they also provided opportunities for you to limit damage to your own site in the event of a bug or exploitable security flaw.
The secret to using this feature properly is to set the proper group ownership on your PHP scripts. If your PHP script is intended to read and write other files in your web space (or related directories), then the script should be owned by the “web” group.
In order for the web server to write to files or directories in your web space, it needs write permissions. As far as the Unix filesystem goes, there are two ways for it to get that: the file can have group “web” and be group writable, or it can have group “me” and be world writable. However, with PHP, that is not sufficient.
On our system, for PHP to be able to write to a file, the group of the file (or parent directory, if creating a file) must match the group of the PHP script doing the writing. If you want a PHP script to be able to write files, you should take the additional step of setting that script’s group to “web.” You’ll also need to make sure that the destination directory for created files has group “web” and that it is group writable.
If this is set up properly, these two steps form a sort of interlock: you specify which scripts are permitted write files, you specify where files are permitted to be written, and the only combination that will succeed is if a permitted script tries to write to a permitted location. Everything else is off limits. This effectively protects the rest of your files from being unexpectedly overwritten by your file-writing scripts, and it prevents other PHP scripts from writing files at all, even if they later turn out to have a security problem that might otherwise allow it.
It’s also possible to get PHP to write files by setting the target file (or directory) to group “me” and giving it world write permissions. However, doing so forgoes all of the above protection, and so it is not recommended. You can also run into problems with this approach if the PHP script intends to create a directory and then create a file in that directory.
All of our member sites have a “protected” directory at the same level as the “public” (aka “htdocs” for older sites) directory that contains your web-accessible material. The “protected” directory cannot be directly accessed via the web, but it has the appropriate ownership and permissions already set for PHP scripts with group “web” to be able to write files. This makes it an ideal, safe place for your site to store and maintain support files without having to worry about what access controls are needed to prevent visitors from accessing those support files directly over the web.
As a final caveat, make sure the PHP scripts you set to group “web” are not group-writable, because that would grant the server permission to modify the script itself, which is generally undesirable.
Update: As of 2012, this post is five years old. Most of it still applies to “Fast” PHP prior to 5.4. The primary difference is that the synthetic “me” user/group is no longer used; you will instead see a unique numeric username for each site.
Sorry, the comment form is closed at this time.