Thursday, 11 April 2013

Securing Wordpress - hardening the webserver

There are three main classes of attack against Wordpress:

  1. Obtaining access to a legitimate Wordpress user account. 
  2. By finding a vulnerability in the Wordpress codebase, including and plugins or themes your users may have loaded.
  3. Comment SPAM.
All of the above lead to the possibility of inserting SPAM links into your blog. 3 is easiest to deal with by comment moderation or installing antispam measures such as Akismet.

If Admin access is obtained in 1 above or via the second attack class, arbitrary PHP could be uploaded (directly or as plugins or themes) allowing effectively arbitrary code execution on the server as the webserver user. This of course may lead to root exploits against the underlying OS. Some danger also exists in allowing Wordpress admins to upload arbitrary unchecked themes and plugins - there are a huge number out there that contain malicious code.

I personally have been bitten by 2 (attacker uploaded phpshell then took over several blogs and used them to distribute a virus via a Windows Internet Explorer attack.

It seemed to me that the first and most general line of defence was to prevent the webserver being able to execute any uploaded files at all, except when the server admin says so.

The first thing is to tell the webserver, Apache 2.2 in my case, to not allow code execution in these directories. these are directories where the webserver and WP need to be able to write files. We only need to worry about paths that the webserver can subsequently serve (and potentially run an interpreter against).
  1. <BLOGBASE>/wp-content/uploads/
  2. <BLOGBASE>/wp-content/plugins/mm-forms/exports/
  3. <BLOGBASE>/wp-content/plugins/mm-forms/captcha/tmp/
1 should be common to all WP installs. 2&3 depend on the plugins running. There of course may be more if you are running different plugins to me.

For each directory above, add this to the apache conf.d/ directory for each path you need to protect (or ensure it is active for each WP site):

<DirectoryMatch /wp-content/uploads/ >
    php_flag engine off
    Options -ExecCGI
    AllowOverride None
</DirectoryMatch>

That may not be the complete solution - I'm checking other options - comments welcome!

Now make the entire WP tree read only to the webserver user (www-data in the case of Debian) and owned by root. Allow the webserver group (also www-data for Debian) write access to the paths above. The owner of these paths will still remain root.

Here's what one of my WP installs looks like to 2 levels deep:

root:root drwxr-xr-x .
root:root drwxr-xr-x ./wp-admin
root:root drwxr-xr-x ./wp-admin/css
...
root:www-data drwxrwxr-x ./wp-content/uploads
root:root drwxr-xr-x ./wp-content/themes-ai1ec
...
root:root drwxr-xr-x ./wp-includes/SimplePie

You get the idea...

Now if we need to unlock the blog to allow updates or adding plugins or themes, all we have to do is:

chown -R www-data <WP-path>

and to lock:

chown -R root <WP-path>

Just to be a little safer I run a few extra bits - which are enough to lock a default install down:

Lock:
chown -R root <WP-base>/
chgrp -R root <WP-base>/
chmod -R o=rX <WP-base>/
chgrp -R www-data <WP-base>/wp-content/uploads/
chmod -R g=rwX <WP-base>/wp-content/uploads/
# and any other paths that need to be webserver writable.

Unlock:
chown -R www-data <WP-base>/
chmod -R u=rwX <WP-base>/

For your convenience, my scripts are available here:
https://github.com/TimJWatts/ddh-blog-utils

No comments:

Post a Comment