Preventing Wordpress BFL/DDOS attacks

I host a lot of Wordpress sites and a common problem is the Brute Force Login attack, which could also be classed as a DDOS attack as it can overload the server or drive MySQL beyond it’s limits.
I’ve taken to using an .htaccess section to mitigate this and it’s quite successful so far, although with some small limitations.

So, first, what is the problem?

Basically the attacker is trying to find the login credentials for the installation in order to have admin rights to the site and use it to launch malware or host trojans. I’ve noticed that most of the spam with links to malware sites are linking to a CMS site, usually Wordpress. The brute force method attempts this by using a script to make multiple login attempts to the site until one works. Running as a script it can try for days or weeks until successful or someone notices. The really smart scripts will have a delay of a few seconds between each attempt and you may never notice them at work. The dumb scripts will hammer away until the server runs out of memory and dies… :(

What solutions do we have?

The best solution is to install OSSEC on the server and make use of the Wordpress module. This will detect multiple failed login or posting attempts and issue a temporary block on the IP.
If it’s not possible to install OSSEC then another option is use the .htaccess file. Here is the section I use for the example domain ‘abcxyz.com’:

1
2
3
4
5
RewriteCond %{REQUEST_METHOD} POST
RewriteCond %{REQUEST_URI} .(wp-comments-post|wp-login|xmlrpc)\.php*
RewriteCond %{HTTP_REFERER} !.*abcxyz.com* [OR]
RewriteCond %{HTTP_USER_AGENT} ^$
RewriteRule (.*) http://%{REMOTE_ADDR}/$ [R=301,L]

What this means in plain English is:

  • if this is a POST request
  • and if it is requesting the login, comments or xmlrpc file
  • and if the referrer is not abcxyz.com
  • or if the referrer is empty
  • then redirect the visitor back to themselves.

Most automated scripts will not have a referrer set and the above will catch them. I’ve seen some of the smarter scripts make sure to first visit the login.php page before making the POST request.

I’m not sure of the wisdom of redirecting them back to themselves, but I wasn’t sure where else to do this. Another option is to simply Forbid the rule.

User names

Whichever solution you use, I strongly suggest not having admin as the administrator username as most attempts at guessing a user name will either use admin or test (see Daniel Cid’s excellent article). So use something like admin2975 or boris instead. Similarly, the username that you publish posts under should never be the admin name. And that user should have limited rights to the CMS.