ACLs, PHP, and suPHP

At my day job, I maintain shared web servers. One of the biggest problems for shared web servers is how to protect different users from each other. In particular, one wants to prevent one user’s files from being read by another user on the same server. This turns out to be a non-trivial problem on a standard Linux server using traditional tools.

The traditional installation method for PHP is as a web server module. This means that PHP scripts run with the credentials of the web server. While this works well enough for simple scripts that do not need to do anything fancy with the file system, it does pose a few risks. The biggest risk is that every PHP script on the system can read every other PHP script on the system. This is necessary for the web server to be able to execute them. While this does not sound dangerous, consider the case of a web site that accesses a database server. Of necessity, the credentials for accessing the database must be present in the scripts for that site. This problem is further exacerbated if the scripts need to accept file uploads and store them on the server. That means that the location where those files are stored must be writable by the web server. That also means that every other script on the server can write to that location, too.

Fortunately, there is another solution for running PHP that does not suffer from all of the above problems. Using suPHP, one can execute PHP scripts as a user specific to the web site in question. This comes at the expense of forking off a new process to handle each request but with today’s server hardware, this is hardly a performance problem. In this case, only the user associated with the web site needs read or write permissions to the scripts and there is no problem with file uploads because they will already be happening as the site user. This method does mean that a malicious script could trash the entire web site rather than just its data files but that is a minor trade to prevent that same malicious script from messing with other web sites on the same server.

The above works fairly well except for the fact that the web server has to be able to at least see the files for each site to serve up the static html files and images. It also needs to be able to decide whether a file is a php script which needs to be run. Under the classic unix method, that means all files end up being world readable just so the web server can see them. This problem is further exacerbated if multiple people need to have write access to the files and further complicated if the files all have to be owned by a particular user for the suPHP scheme.

Enter a not so new technology known as POSIX ACLs. These are an extension to the classic unix file permissions to add the notion of access control lists. That means you can give a subset of users read permissions to a file, another subset write permissions, another subset both, and yet another subset execute permission. Further, you can define the default access permissions for newly created files within folders. It is a very powerful tool, which if used correctly can greatly improve security on a shared server.

Using ACLs, one can limit read permissions to each folder to just those that need it. That means it can be limited to the web site user and the web server. No other user can read those files at all, even though they can still be served to the internet by the web server.

The default ACLs on folders allow an even more useful situation. In the case of multiple users having write access to a folder, they can all create files (which will be owned by them) that can automagically be writable by everyone else without any special effort. While a similar effect can be accomplished with unix groups, suppose one user needs access to fifty different folders? That stresses the limits at best and exceeds them at worst on most systems. And even then, it requires that the user remember to make the files group writable.

Unfortunately, as wonderful as the ACL scheme is, suPHP generally expects files to be owned by one particular user and it does not understand ACLs. Furthermore, most other software does not yet understand ACLs, including PHP itself. That means, to make everything work smoothly, some software needs tweaking. That includes such wonderful software as suPHP. It likely also includes whatever scheme is being used to back up the server.

At my day job, we added a small patch to suPHP (not publicly released) to allow multiple users to be deemed safe for a particular file without relaxing the security checks substantially. We also had to update a couple of system maintenance tools to pre-release versions. However, the configuration has been working well enough for a couple of years.

We have had the odd wacky problem pop up due to random interactions between PHP, suPHP, and our system configuration. One of the biggest is file permissions after calling move_uploaded_file() in PHP. With older versions of PHP, the file does not gain the default ACL for various reasons. This problem is easy to resolve when discovered, though. Ensure the temporary upload directory is on a different partition or under a folder that has the same default ACL as the ultimate destination. This particular problem affects systems not using suPHP as well. Later version of PHP have been updated to behave better but there is no guarantee some genius PHP developer will look at the move_uploaded_file() code and say, “I can make that simpler by doing…” and undo the fix.

The point of this ramble is that there is no need for shared hosting to be insecure by design. POSIX ACLs have been around (and supported by Linux) for a fair number of years now but almost nobody is using them. Ubiquitous tools like tar often don’t support them. The question is why not? If one sysadmin can work out how to use them, why cannot teams of developers on larger projects figure it out? Why are so many web hosters using much more baroque and less clear tools to accomplish the same thing?

Leave a Reply

Your email address will not be published. Required fields are marked *