Regular security checks – what steps should be included?

There are heaps of tutorials explaining how to secure a WordPress installation, but I couldn’t find any that listed out steps for weekly or monthly security checks. What are some steps that you recommend I perform on a regular basis to keep WordPress secure (besides updating core and plugins)?

Here are some things I’ve implemented already:

  • Forced all other users to only run as Editor
  • Deleted unused themes and plugins
  • My theme is custom written, so I know that there aren’t any scripts like timthumb.php in it. It’s also powered by Genesis which features automatic theme updates.
  • Change my admin password every few months
  • Automatic backups with VaultPress

The main reason why I’m concerned is because my client’s hosting provider is not very WordPress friendly and I have to run with 777 permissions. However my client is the only site on the box.

Anything else I should be doing regularly to make sure the site is locked down? Thanks

Solutions Collecting From Web of "Regular security checks – what steps should be included?"

First: Remove those 777 permissions. You need this only in cases with conflicting ownership. Try to run PHP as FastCGI – for example per .htaccess:

AddHandler php-cgi .php
# or
AddHandler php-fastcgi .php

Set up a notification mail for every 404 request. You will be surprised how many attacks the average blog gets each day. Wait … I just published my 404 notifier on GitHub:

Plugin T5 404 Tools repository · Download latest version

Also, install a plugin to prevent password guessing. I use Login LockDown with a lockout length of 45000 minutes.

Read your log files regularly. There is no better way to get all critical information.

There are also plugins which check all files for changes. I had just problems with those: slow, a lot of misleading information, just too much noise. But if your site hasn’t that many files it may be an option. Test it.


One note about file permissions: Usually you don’t want anybody to send DELETE or PUT requests to your site, especially when something is set to 777. 😉
To restrict the allowed request methods to HEAD, GET and POST, add this to your .htaccess:

<LimitExcept HEAD GET POST>
order deny,allow
deny from all

I recently mentioned this on Security Stack Exchange, but I’ll repeat this here. I believe it’s directly on-point.

I recently published a WordPress plugin that you might find helpful. I call it Foresight. It’s available for download from the WordPress Plugin Directory:

(It’s also mirrored on github.)

The plugin is pretty simple: all it does is load a “Foresight” link into the “Tools” section of your sidebar when you’re logged in as an administrator. If you click on that link, you will be presented with a series of tabbed iframes, each of which displays the newest WordPress exploits found at the following popular exploit trackers:

  • CVE
  • CVE Details
  • Exploit DB
  • Secunia
  • Inj3ct0r

This tool isn’t particularly fancy or anything. (I think of it as a dumb tool for a smart administrator), in that it’s ultimately just loading a bunch of iframes. When you’re doing your periodic security review (daily, weekly, monthly – whatever), I’d recommend that load it up and browse the exploits, keeping an eye open for exploits in the WordPress core or plugins that you use. If you find such an exploit, you can then make an intelligent decision regarding how best to proceed.

I named it “Foresight” because it’s intended to give you foresight as to what exploits might be fired at your blog in the near-term. Thus, if you keep in eye on it when you do your periodic security reviews (or even just casually on a more frequent basis), you’re going to have a leg-up on administrators who don’t watch the exploit trackers. That, in turn, will hopefully keep you from being “low hanging fruit” (from the hackers’ perspective), which is a good thing for the health of your blog.

In this code there is no need for putting HEAD:

<LimitExcept HEAD GET POST>
order deny,allow
deny from all

HEAD is implied by GET — see documentation

Also no order directive is needed, so most efficient would be:

<LimitExcept GET POST>
deny from all