This security article delves into various best practices and other techniques used to protect a WordPress website from being compromised. It tries to cover a lot of ground, but as with anything in security, there is hardly a way to 100% protect a website from intruders. Compare this to a house – a house without a fence could be easily compromised. Well consider the internet one of the most unsafest places where you simply cannot afford to build a house without a fence. But a fence will only protect you from one aspect – there are numerous other aspects to consider. Consider any or all of them depending on your needs and ability to administer security.
The latter parts of this article assumes you know where to find your websites’ log files and that you know how to use the command line. You will also typically require root for some of these commands.
- Only use well known and well supported plugins
- Do not choose easy admin passwords
- Do not share admin passwords
- Make sure WordPress and it’s ecosystem is up to date, including:
- Plugins must be up to date. Weekly updates are great, and monthly is stretching it. Normally when a hack comes out it’s day if not hours before it’s exploited
- Core WordPress must be up to date
- Make sure your themes are up to date. This can be really hard, so see the next sections
- Make sure you are using a supported and up to date version of PHP
I’m scared my WordPress site breaks when I update
This is a common problem. Perhaps your web designer has moved on. Perhaps your theme designer isn’t in business anymore. Perhaps your attitude for years has been “well it’s working and it hasn’t broken yet, so why should I update it?”. Some sites are just poorly designed and can’t really be updated. In best practices we mentioned use well known plugins, if you haven’t been doing so, perhaps your plugin isn’t updated anymore.
My advice is compare the cost of a compromise to the cost of finding some to help you upgrade the site. If you don’t have a strong enough commercial network, we suggest sites such as Fiverr and Upwork where there are many competent WordPress security experts for a low cost that can assist with the task.
To understand the math of a WordPress compromise, consider this:
A hacked site can cost you 100s of even 1000s or even millions in lost revenue and reputation. Finding a contractor to update the site will cost less.
If you are still scared something is going to go wrong, do a backup and restore to another location and do the upgrade there. Or take a snapshot. Be creative but be pro-active and don’t leave it till it’s too late. Once everything is up to date it’s much easier to keep up. And these days WordPress can keep itself mostly updated for security, whereas plugins are also quickly moving in that direction.
Protecting a WordPress Website From Brute Force Attacks
The are numerous good plugins such as WordFence which will do the heavy lifting for you. Typically a brute force attack on WordPress targets two files, namely:
The brute force attacker will repeatedly try and log in or repeated try and use the WordPress API to compromise the site. You might not know it until it’s too late.
How to detect a Brute Force WordPress Attacker by using Top
If you don’t use a plugin such as WordFence, then
top is your friend when it comes detecting a brute force attacker. What you’ll notice in top that some of your websites seems to be constantly busy.
[email protected]:~# top top - 12:00:51 up 196 days, 20:37, 1 user, load average: 0.00, 0.05, 0.11 Tasks: 524 total, 1 running, 460 sleeping, 0 stopped, 0 zombie %Cpu(s): 0.4 us, 0.2 sy, 0.0 ni, 99.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.1 st KiB Mem : 16424888 total, 1377604 free, 5318696 used, 9728588 buff/cache KiB Swap: 0 total, 0 free, 0 used. 9421996 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 7276 sitenamewww 20 0 330976 71404 53396 S 1.0 0.4 0:04.53 php-cgi7.3 16475 sitenamewww 20 0 331580 74868 56212 S 0.7 0.5 0:23.75 php-cgi7.3 9955 sitenamewww 20 0 613084 48200 9932 S 0.3 0.3 6:50.87 php-cgi7.3 13090 root 20 0 1644596 44036 10412 S 0.3 0.3 1:29.59 fail2ban-server
When one looks at the above output you notice
sitenamewww three times in a row. At first glance one might be temped to think that this website is getting a lot of traction, But how do you confirm that? The next thing to do is to output the log file.
How to spot WordPress Brute Force attackers in the log file
tail command as per the example below on the busy website:
tail -f /home/sitenamewww/log/access.log x.32.175.72 - - [30/Aug/2020:05:18:36 +0200] "GET /wp-login.php HTTP/1.1" 200 1105 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0" x.32.175.72 - - [30/Aug/2020:05:18:36 +0200] "POST /wp-login.php HTTP/1.1" 200 1498 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0" x.32.175.72 - - [30/Aug/2020:05:18:36 +0200] "POST /xmlrpc.php HTTP/1.1" 200 217 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0" x.70.179.71 - - [30/Aug/2020:05:37:07 +0200] "GET /wp-login.php HTTP/1.1" 200 1105 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0" x.70.179.71 - - [30/Aug/2020:05:37:07 +0200] "POST /wp-login.php HTTP/1.1" 200 1498 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0" x.70.179.71 - - [30/Aug/2020:05:37:07 +0200] "POST /xmlrpc.php HTTP/1.1" 200 217 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
If you see repeated attempts from the same IP address to the same file you are dealing with a brute force attacker. The next section explains how to use
Fail2ban to read these log files and take the appropriate action.
How to use Fail2Ban to Stop Bruce Force WordPress Attackers
All security events are logged. That includes SSH logins, file access, mail server authentication, etc. Fail2Ban is amazing software that is easy to use, and helps by analyzing these log files.
There are two places that you have to adjust in Fail2Ban to read WordPress compromises:
Please note file paths might be different on your system.
jail.local is the master index of which jails to load. Here you can add another configuration section at the end:
cat /etc/fail2ban/jail.local [wp-sitenamewww] enabled = true port = http,https filter = wp-sitenamewww logpath = /home/sitenamewww/logs/access_log maxretry = 4 bantime = 86400
Note we have prefixed the site name with
wp- This is only for cosmetic reasons as jails are generally quite large as the Fail2ban system does a lot of other work too. Most of the settings are self explanatory. In the above configuration, the log file is read all the time by
fail2ban Once 4 failures occur, the script kiddy is shown the door for 24 hours.
Let’s move on to the actual jail:
[email protected]:/etc/fail2ban# cat /etc/fail2ban/filter.d/wp-sitenamewww.conf [Definition] failregex = ^<HOST> .* "(GET|POST) /wp-login.php ^<HOST> .* "(GET|POST) /xmlrpc.php
There you have it. A regular expression checks any host accesses HTTP