Tightening up on #WebsiteSecurity should be the next priority for your #WordPress site.

If your WordPress site has been up more than 2 hours, you’ve probably already collected a bunch of spam attempts.  There are several tools out there to help you out with spammers and hackers, but few do better in protecting against the former category, IMO, than Anti-Spam by CleanTalk.  Straight up, it is not free, but they have a short demo period that will likely impress you.  On top of that, it is only $8.00 per year.

However, assuming you’ve been following along, by now you’ve set up a site with ssh and ufw as the firewall.  The firewall is the bare minimum, and it is far from foolproof.  What you need to do is setup a tool that detects someone banging against your site trying to guess logins.  That is what Fail2ban is for.

Since installing a mail server also requires Fail2ban, IMO, I suggest you run through “Setting Up iRedMail for Email Server” first and then modify the instructions below as needed.

DigitalOcean has an excellent tutorial about the basic setup in article “How To Install and Use Fail2ban on Ubuntu 14.04“, although it goes into using iptables, which was covered by setting up ufw, and Nginx, which can be ignored on an Apache run site.

I highly suggest setting it up initially to email and log for troubleshooting purposes, just in case something was typed wrong.

One deviation is that I prefer longer, much longer, times for bans.  Consider that some bots are now smart enough to try again in 30 minutes in order to work around smaller time units of bans.  However, later on we’ll look at how to ban repeat offenders, so be careful about making the bans too long and the recidive jail ineffective.

Once it is setup and working, strongly consider installing the WP Fail2ban plugin.  Be sure to follow the directions, though, as the FAQ contains information needed for Debian based systems as well as some necessary additions.  For example, just ban anyone using “admin”.  Hopefully, you have changed your admin name to something else by now, for practically all bots will try that one first.  Also, be sure to check out the information on preventing user enumeration in the plugin FAQ.

Test, test and test some more. 🙂

At this point, you are fairly secure, but there is one more area I would tighten up in.  The default Fail2ban setup will ban an IP address for a specified period of time, but then afterwards it will unban it.  This will kill about 75% of the bot traffic, but there always seem to be a stubborn few that will repeatedly get banned over and over again.

There are several tutorials out there on “persistent” banning, and a lot of them have merit.  It must have become such a popular request that newer versions of Fail2ban have a recidive filter.  It isn’t quite the same thing, but you can ban addresses for very long periods of time (for example, one year), although the longer periods of time require an understanding of how Fail2ban work (as well as the other “persistent” ban methods, actually).

At first glance, a persistent ban might seem more desirable, but consider the fact that most bots are on infected machines and many times without the user’s knowledge.  Also consider that most IP addresses are dynamic in nature, and it is unlikely for any given location to have the same address forever.  In the end, you may wind up blocking more than a few “innocent” IP addresses.  This is why I decided to go ahead with the built-in rather than the extraneous methods, as tempting as it might be.

Some pretty good instructions are available on DGhost’s Blog in the article “The power of Fail2ban“.  However, some extra verbiage might be helpful in a couple of areas.

The maxtries in the recidive jail refers to the number of times the IP address was banned.  I don’t know.  Maybe that is obvious to everyone else, but it didn’t seem all that clear to me initially.  So, as an example, if you set it so that 3 bad logins to ssh, or IOW maxtries = 3 in the ssh jail,  bans it, then that is only one ban to count towards the maxtries in the recidive jail.  If you set maxtries in the recidive jail to 5, then 15 tries within the specified period of time, findtime, will trip the more extended ban.

The other area that is not so obvious is that the logs rotate after a given time, and so some IPs can be reset if the server is rebooted or one of the associated services restarted.  So, you really need to do some additional checks and/or editing.

First, make sure that /etc/fail2ban/fail2ban.conf has the correct loglevel.  It should not be set to 4, as it warns in the file.  Normally, loglevel is set to 3.

Second, check out /etc/logrotate.d/fail2ban.  Normally, it is set to rotate weekly and keep the last 4 logs.  So, if you ban anyone longer than a month (specifically, 28 days), this setup will not do what you want it to do.  In that case, you will need to increase the duration and/or rotate parameters.

Be sure to doublecheck that the ban time for a particular jail is not longer than (or even impractical for) the findtime for the recidive jail, else it will not work.

This setup will not solve every problem for every situation, but it should be suitable for the vast majority of WordPress systems.  The nice thing about doing it this way is that it is outside of the PHP environment, so banning works even before tying up extra resources.  If they get past that layer, the security of CleanTalk’s Anti-Spam will kick in to check for comment spam and blacklisted registrants.

There are other security plugins for WordPress, but be sure that they will work with Fail2ban and Anti-Spam before putting them on a live system.  One such plugin is WP fail2ban.  Be sure to check out the FAQ page, however, as Ubuntu will probably require a port entry for the wordpress section.

One last thing that you will want to do, but you might want to leave it until last just in case you need it as a last resort, is to look at How-To Geek‘s article “Security Tip: Disable Root SSH Login on Linux“.  Having this open is a nice fallback when doing initial setup, but it is also a big security hole.