Webserver (apache) security

Generally my experience with apache has been very good. It’s a decent web server and works very well, is well supported and generally doesn’t suffer too many problems. For an ISP though, there are a few issues: php and other sub-processes/modues runs with the privileges of the apache user. So what is so bad about that?

Firstly, many users loves this:

chmod -R 777 htdocs/

Now, the words that comes to mind to describe anybody that gives that kind of advice can only be described as extremely rude. However, there are legit areas where one wants to give the apache user write access, eg, an uploads/ folder. As a normal user you have no way to only give the apache user additional rights, and more specifically, even if you could only give the apache user additional rights, you’re still stuck with a catch twenty-two problem: All code from all websites also gains write access here, and if that write location happens to be publicly requestable we can run code in the context of other websites, and equally bad, we can get code executed on browsers in the context of the other website.

Whilst this situation has been long-known to me I just didn’t realize quite how serious it really is until recently. At least, not until I saw the mpm_peruser stuff and started thinking about why you would need this. No seriously, it’s one of those things that just passes by you, and one kind of tends to assume that everybody is inherently good. The problem is simple: It only takes on dumb mistake, along with one (ab)user with malicious intent, to take down all sites on a server.

There has recently appeared one additional firewall rule on my servers:

iptables -A OUTPUT -m owner –owner apache -o ! lo -j REJECT

This makes it much harder to get that “arbitrary” code onto the server since apache can no longer go about downloading the code and it now has to be pushed to the server via some upload page (pages not using $_FILES are now much harder to exploit, and it should not affect general web sites too much, and those that it does will complain soon enough and you can open the specific destinations for those easily enough).

The original problem is still not solved however. Ideally we want to run each website as it’s own user so as to prevent the kind of screw-ups where a hole in one website can result in compromise for another. In short: We NEED to get rid of the chmod -R 777 htdocs/ syndrome.

mpm_peruser has a nifty idea, it basically breaks up the virtual hosts into sets handled by the same user/group pair. The one sample configuration looks like this (http://www.telana.com/peruser.php):

# Processor <user> <group> <chroot>
# chroot is optional
Processor alice users /home/alice

<VirtualHost alice.com>
ServerName alice.com
# normal vhost configs
<IfModule peruser.c;>
# this must match a Processor line
ServerEnvironment alice users /home/alice
# these are optional – defaults to the values specified above
MinSpareProcessors 4 MaxProcessors 20
</IfModule>
</VirtualHost>

At first glance this looks just fine. In fact, this looks quite good actually! So where is the problem? Dead simple, the user is uploading as alice, this means that apache now gained the ability to chmod and chgrp the files for this virtual host! So we are effectively giving the code of this website rwx permissions to the entire home folder /home/alice (albeit, the process will see it as / – I’m still not sure how php handles the chroot()’ing that’s happening in the example above). This is problematic!

To clearly see the effect of this – there is a reason why .php files are generally kept at permissions 644, and that is that is the general write/execute exclusion principle. You want any given process to only have write, or only have execute permissions to any given object. Once again, this is a well known concept and is used by technologies such as AMD’s NX-bit and stack smash protectors to reliably protect against heap-corruption and stack overflow arbitrary code execution vulnerabilities. The same principle should apply to apache: It must either be able to execute a file (script), or write to it, but not both. For this reason I usually locate upload/ folders OUTSIDE the htdocs tree for my own code. And I’m beginning to take that principle even further, even include/ folders needs to get out of the tree. This unfortunately breaks php’s safe_mode.

Then I realized that we can hit some middle ground. We can let apache run as the apache user, but only vary the group. This way if we configure a website (say uls.co.za) to be controlled by the user “uls” then we can create that user with a dedicated group called uls, so all files under /home/uls/vhost_uls.co.za/htdocs/ are owned uls:uls. We can take the chroot to this folder, or /home/uls, either way, I’d probably go to /home/uls/vhost_uls.co.za/ in order to allow storing out-of -htdocs folders such as include/, and uploads/ depending on the website, as well as perhaps config files.

So why do I call this middle ground? Well, simple, even without the chroot() I’d be quite happy with this. The reasons as follows:

  • uls.co.za can be set up to be served by apache:uls
  • /home/uls is 750, as is /home/uls/vhost_uls.co.za and everything in it.
  • directories where apache may only read is chmod 750, for write 770, for files read-access is 640 and write access is 660.

Apache must not actually be a member of the uls group. Now, any other website (say other.co.za) will run as another pair (eg apache:other). Since it’s not the apache user that gained read/write access above but the uls group, other websites can’t cross-attack. At the same time we can still reliably control where apache is allowed to read/write.

In order to enforce the mutual write/execute exclusion is trickier, and for this we need some help from the individual coders who must know to get their upload/ and include/ folders OUT OF htdocs/ In other words, chmod g-w -R htdocs/ must end up not actually modifying anything.

The developers that uploads content to the system should also be made aware to not use chmod 777, but instead 770. This can (afaik) be enforced by many of the ftp packages out there. The default umaks for these users should probably also be set to 027.

Time to go experiment…

Comments are closed.