Update 11/3/2017:
Need to learn how to secure your WP site? Check out our latest guide on vulnerabilities, best practices and protection.
Update 9/14/16:
We released a new guide that provides better instructions on how to clean a hacked WordPress site using the Free WordPress security plugin.
The biggest problem today with most content management systems (CMS) and web applications is the adoption of what we call the “Win95 security model”. Do you remember the Windows 95 security model? With everything running under the admin role? No separation of privileges? No separation of processes? One vulnerability and you take it all?
Confused as to how this relates to today’s CMS’s (including WordPress, Joomla, and others)? Let us explain using WordPress as an example (most popular CMS out there):
- The user that is running WordPress (including any plugin or theme) can modify any file on the site.
- Any URL can be accessed directly, even when they are not supposed to be used that way.
- The admin panel (wp-admin) runs in the same domain and within the same codebase/permissions as the rest of the application.
- Admin users can install any plugin/theme, which can then modify any file and change anything in the database
- Most users log in as admin, even though they don’t need “admin” access most of the time.
- Every plugin and theme has the same level of access (and permissions) as the core of the application (no separation of privileges).
Yes, this is the default and most common way to install WordPress, specifically on shared hosts. Why you ask? It is easy to install and configure, but definitely not to be confused with the correct or secure approach to installing any application.
Imagine, it’s like having one username and password for all your accounts… oh… wait…
Improving Your Security Posture
I should preface this by saying that this won’t apply to shared environments, sorry… đ
But if you’re on a dedicated (or VPS) environment you’re in luck so read on.
1- Correcting Server Access Control
The user running WordPress (generally www-data or apache) should not have permissions to modify any file.
That protects you in case there is a vulnerability in any plugin, theme (or even in WordPress itself), since they won’t be able to modify anything.
Fortunately, there is an easy fix but one that many won’t use. In most instances WordPress is running as apache/apache, default webserver roles, and what you want to do is create a new user that will assume that role, disallowing the webserver user from having those rights.
To do this, this is what you have to do:
# adduser wordpressuser
# cd /var/www/pathtoyoursite
# chown -R wordpressuser .
# chmod -R 750 .
# chgrp -R apache .
This makes it so that only the wordpressuser user is the only one with with write permissions to your site directory.
Now, we all know that the only directory that needs to be writeable is the uploads directory in WordPress. So, to accomodate this we make some slight modifications that adds a rule allowing our beloved Apache user to write to that directory:
# chmod -R 770 ./wp-content/uploads/
Now, to ensure that we’re taking every precaution possible we turn to our .htaccess file to protect us from the injection of backdoors. You’ll want to create the .htaccess file inside the /wp-content/uploads directory:
php_flag engine off
and
<Files *.php>
deny from all
</Files>
InfoSec is about risk reduction, by taking these additional steps you have now greatly reduced the inherent access control risk associated with default WordPress installations. Do note however that this is not isolated to WordPress, these can also be applied to Joomla, Drupal and other such CMS applications.
Disclaimer for my colleagues: Doing this change will require you to take a more proactive approach to the administration of your site. You won’t be able to use the pretty GUI’s WordPress offers you, you’ll have to manually update your core, themes, and plugins. But trust me, it’s ok, it’s only slightly inconvenient.
2- The Accessibility Challenge
WordPress has many files, but very few of them need to be accessed directly by the users. To prevent files being accessed directly, we recommend blocking direct PHP access to /wp-content with the following additions to your .htaccess file:
<Files *.php>
deny from all
</Files>
An example of how this would have been useful, let’s think back to the TimThumb outbreak last year or even this year’s Uploadify discussions. Killing the execution of PHP in /wp-content would have and will protect you from both these known vulnerabilities.
3- Correcting Application Access Control
The admin panel (wp-admin) runs on the same domain and same privileges as the rest of the application. In a perfect world, it would be isolated, but to minimize issues, we recommend adding two restrictions to wp-admin access (via .htaccess):
3.1- Only allowing certain IP addresses:
order allow, deny
allow from IP1
allow from IP2
deny from all
3.2- Adding an additional user/pass (pre-authentication):
AuthUserFile /var/www/mysite/.htpasswd
AuthName “Site private access”
AuthType Basic
require user myuser2
By adding those two restrictions, you isolate wp-admin, requiring additional permissions and again, improve the security model. Hope you’re starting to get the trend of the post.
4- Restrict admin access to only required users
On the old days of Windows, everyone was admin. Don’t do the same on WordPress. If someone needs to write posts, they only need author permission. If someone needs to edit/post articles, he only needs editor permission.
There is a reason that out of the box WordPress provides varying degrees of roles and associated permissions, use them. We realize they may be a bit complex and difficult to understand, hopefully this helps:
- Administrator – can do everything
- Editor – They write stuff, more importantly they can publish
- Contributor – The Author’s cousin, but they can’t publish
- Subscriber – They follow your rants about your favorite past times but can’t do anything else
Logging in as Admin for every task is foolish, don’t be that person.
Summarizing the journey
Above we addressed 4 steps, that if employed, covers 5 distinct sections in the security model I prefaced the post with:
- The user that is running WordPress (including any plugin or theme) can modify any file on the site
- Any URL can be accessed directly, even when they are not supposed to be used that way
- The admin panel (wp-admin) runs in the same domain and within the same codebase/permission as the rest of the application.
- Admin users can install any plugin/theme, which can then modify any file or change anything in the database
- Most users log in as admin, even though they don’t need “admin” access most of the time
By employing some, ok all of the recommendations above you have effectively disabled WordPress from modifying its own files, admin users no longer have write permission to all files, and the themes / plugins can’t modify everything. Why are these three areas important? Well, because they are the most exploited vectors to date.
If you think this is all, you’re wrong, I would prefer to see updates where the plugins / themes no longer have the same level of permissions as the core of the application and their ability to have full database access (read/write) are dismissed, but, I don’t want to give the appearance that I’m paranoid.
But seriously, in a perfect set up, you would have at least 2 database users, one configured on wp-admin with write access, and one for the rest of the site with only read access. I’m just saying..
If you have any questions or more ideas about this model or how to improve it, let us know.
14 comments
Great post. I perform some similar measures by setting the immutable bit on the web root, even down to the upload folder. It’s not often that I need to upload files so I can just change it on that folder when I need to.
I did run into a few issues with plugins that cache content to speed things up, but those issues can be easily worked around.
I also have digest authentication set up to protect the wp-admin part.
Upgrading is not a problem. Just throw the steps to “un-lockdown” the site, perform the upgrade, then lock it down again. Works well.
I meant:Â Just throw the steps to “un-lockdown” the site into a script, perform the upgrade, then lock it down again. Works well.
Nice post. I did like the way of explaining all the measures. We at cisin performing similar task related to wordpress. Do visit the given link once
http://www.cisin.com/wordpress-development-india.htm
Â
Some really good tips here. Don’t forget that with POSIX ACLs you can get even more fine grained control over permissions. Also, I question the usage of basic auth for protecting wp-admin unless you’re using ssl.
Some great suggestions, Daniel. Thank you. Based on your Accessibility Challenge recommendation I applied a similar deny rule in my nginx configuration to block execution of php files under wp-content. Simple, yet provides huge protection.
I was wondering why you wouldn’t use the SSH method to update automatically? I find that the extra steps to configure this setup are well worth the effort to gain the convenience of updates via the GUI. It’s definitely nice knowing all the files under the document root aren’t writeable by the web server process, and using SSH to do updates allows us to use the admin interface the WordPress folks have done such amazing work on. I’m interested to get your viewpoint.
Thanks,
Victor
Hey Daniel, thanx for these nice quick fixes, however i found I’m having a problem with one of the .htaccess fixes. In the fix for the wp-content folder. When i add the .htaccess file with the code below:
deny from all
I notice the my images are no longer showing up. I think this is because a php file is used to create the thumbnails on my site. I’m not sure which php file it is but i’m guessing allowing only that php file should fix the problem….
I don’t know if this is correct .htaccess syntax but it does seem to show the thumbnails now after i added thumb.php(file i found in my theme folder) to my .htaccess file. The new file looks like this:
deny from all
Allow from all
“we all know that the only directory that needs to be writeable is the uploads directory in WordPress”.
Um, no. You need the plugins folder to be writeable if you want to install plugins from the WordPress admin. You need .htaccess to be writeable if you want pretty permalinks.
How about a similar article for those using Nginx (as .htaccess is different).
Also – some of us use InfiniteWP and MainWP for updating – How can we allow that? (I have 50 sites and manual updating is a NO thanks).
Cheers
I find it useful to have a script that “unlocks” my hardening for temporary administration. One function does stuff like changing the perms back so Apache is writable by the running user, modifying .htacess rules, etc. The other function puts it all back. So I just unlock it before using the easy update methods within WP. As a fail safe, a cron job locks it back down while I’m sleeping should I forget.
Comments are closed.