A few years ago PHP developers decided to address a problem not their's to solve by implementing a configuration directive called safe_mode. To those unfamiliar with this wondrous invention, this setting is primarily intended to provide file access limits to prevent users from accessing files that do no belong to them. This supposedly should make it impossible to access files of other people in a shared server environment, a common operating environment for PHP where PHP runs as an Apache module and as such has read access to all files accessible by the webserver regardless of the owner. When enabled, safe_mode will perform a uid/gid (user id and group id) check on the file/directory to be accessed and compare it to the uid/gid of the script that is trying to access the file. If the two match then the file operation will proceed as normal and in all other cases it will fail. In theory this is a fairly simple hack to a problem that is not otherwise easily addressed without significant performance penalties such as running PHP in CGI mode, whereby the scripts are executed under the user's own user/group id. However, as with virtually all hacks there tend to be unforeseen problems that further prove that temporary solutions only escalate problems. So let's examine the safe_mode problems and hopefuly demonstrate why it should be avoided.
The first problem that is really more of a bug then anything else is that safe_mode requires that anytime PHP or the underlying libraries used by PHP extensions perform a file access a uid/gid check be performed. While it is not difficult to do and wrappers exist for it, every single release since this functionality was introduced had fixes for missing safe_mode checks. While this is rarely affecting commonly used functions and extensions, it does not change the fact that many functions could bypass these limitations. In fact an overview of extensions inside the PECL repository shows that many of them do not in fact perform these checks. Some of these extensions are fairly popular and I've seen them in use by fairly large number of webhosts. This means that ISPs that assume (yes, we all know what they say about that) that safe_mode works are really misleading themselves unless they actually perform an audit on the PHP extensions available on their servers.
The second and perhaps the most critical problem is the implementation of the functionality and how it effects the users. Consider the following situation, you have a script script that generates a frequently accessed page, parts of which you can safely cache. The simple thing many developers would do, is pregenerate that data and store it inside a file that can be simply included saving a significant chunk of processing time needed to generate that output. Well, under safe_mode, creation of this cache file, would work, but accessing said file would fail. The reason for the failure is that the original script owned by you, does not match the uid/gid of the file it created because the file was created with permissions (uid/gid) of the webserver who PHP runs under. Effectively safe_mode limits you to accessing files that you uploaded via FTP or ssh and had previously prepared, so that you can only append (or truncate and append) to them. This quite frankly makes performing virtually any file operation a big pain in the ass. Fortunately, there is a bypass, which puts the whole concept of "safe" mode into question.
You can upload the PHP scripts you intend to have to perform file operations via another script, thereby giving you a script who's owner/group is the webserver. This script will then be able to edit/add/remove files and directories since all items involved are owned by the webserver. However, you first need to prepare the directories the uploaded script will be writing to, since existing directories do not have a matching uid/gid (they are owned by you). So, you actually end up with a rather involved process for something as simple as file operations, which looks something like this:
1) Create script uploading page
2) Upload "work" scripts
3) Create directories where the work scripts will write to.
Some ISPs try to alleviate the problem by regularly chowning user's files & directories, however that not only can put a big stress on a server with many files, but create a undefined situation between the time new files are added and chown runs.
Now here is the interesting part. If other people use the same safe_mode bypass I've described or just write to files with PHP that are later accessible by the webserver (create static html pages) other users on the server will be able to read and possibly even write (file permissions allowing) to these files by uploading a PHP script via the webserver.
The third problem with safe_mode is it also performs other "security" limits such as disabling shell_exec and backtic execution. It forces automatic escaping of arguments to exec(), system(), etc... commands making them virtually useless especially when commands with arguments needs to be executed. At the same time it does nothing to prevent a savvy attacker from uploading a script or an executable to a "safe" directory and then executing it with the webserver's permissions. Even if execution functions are disabled a creative person would realize that using cron facility (offered by most ISPs) it is possible to run scripts of your choice on the server. Since virtually no ISP actually bothers to prevent world read access to other user's directories you'll still be able to read their data. Better yet, many of them would have weak file permissions potentially allowing you to edit/delete and even store files in other people's accounts. Pretty "neat" trick if your ISP limits disk space
Perhaps, my biggest pet peeve pertaining to safe_mode is that it is not really safe, merely offering an illusion of safety that does not stand to closer examination. A creative attacker will be able to easily to bypass it. If not through PHP then through mod_perl, mod_python or even a CGI script/binary, most ISP do not limit their users to just PHP and developers of other languages stuck to language development and not addressing security issues best addressed by webserver software or server configuration. The misleading nature of safe_mode tricks Admins who don't know better into taking this "shortcut" instead of implementing proper security measures, leaving their systems and their user's data for the taking by even by a novice attacker. Given the number of implementational problems users who come across this flooding the ISPs support resources with their requests costing the ISP money and amount to much frustration by the users who's software often will not work properly under safe_mode forcing them to switch ISPs who do not use safe_mode. This also spills over to PHP developers who are continually harassed by admins and users who seek enhancements, want fixes, etc... for this hack.
So is the real solution to the problems of permissions for file access on shared hosts. By far the best solution is to give each user a virtual server, on which they are an admin. This means effectively creates a separate system for that user, that is completely independent from other users on the system. Virtual servers are surprisingly efficient configurations because in the end users tend to run the same binaries that share the same memory space.
Another alternative is to use CGI or more efficient Fast-CGI in combination with security permissions (umask) that would prevent files created by the user from having a world read and have them owned by the Apache group. A simpler trick could be setting a 711 mask on user directories preventing the listing of files in those directories making file retrieval by unauthorized users extremely difficult.
The last security measure can be found in PHP itself and is called open_basedir. This INI directive allows you to restrict file accesses of a particular user to a series of directories.
both open_basedir and safe_mode result in a perfomance loss in file operations. This is due to PHP having to fully resolve the path everytime and in the case of safe_mode also stat it to fetch the uid/gid of the file. So as far as safety hack go, open_basedir is much more perfromance friendly then safe_mode.
As far as implementing open_basedir for many virtual hosts, you just modify the script that creates the virtual host entries to add a php_admin_value open_basedir "/user/home/dir/" into the vhost. If you are doing this by hand then either you have a very small customer base, or have too much time on your hands
If you have 10k customers already added to httpd.conf (or similar) on several servers, this operation might not be as easy as you'd expect. It's a little too late for PHP to say "Hey, we broke it, you fix it".
Would you then recommend you turn safe_mode off and only use open_basedir?
open_basedir would also have to allow access to session.save_path, because of sessions?
In any case, is there no way for PHP itself to fix this? What about the chown stuff I mentioned?
First of all you can specify a different temporary & session directory for each user. That is what I do on my servers and the ISPs from who I've configured Apache/PHP. This way each user has their own data separate from other users. Writing a short script to modify existing httpd.conf takes no more then 1-2 hours, heck you can even use PHP for that. So saying that this would be terribly difficult on large (previously configured) hosts is a bogus argument.
Take a look at my PHP patch at:
it allows PHP scripts to operate on files/directories owned by ther web-server user if they reside in directories, owned by "the right" user /or if they reside in directories owned by web-server user and parent directory is owned by the "right user"/.