Table of Contents

WordPress Brute Force Attack Protection

A ‘brute force’ login attack is a type of attack against a website to gain access to the site by guessing the username and password, over and over again. WordPress is the most popular CMS and therefore it’s a frequent target of this type of attack. The wp-login.php and xmlrpc.php pages are the most common target of brute force attack by POST method. WordPress doesn’t have any built-in protection to prevent this, hence the need for a third-party solution.

Starting with version 5.2.3, LiteSpeed Enterprise has a built-in WordPress brute force attack protection system. It will protect shared hosting WordPress environments from large-scale brute force attacks, which have the potential to bring down entire servers.

WordPressProtect is also built into LiteSpeed Web ADC.

How Brute Force Protection Works

The newly introduced WordPress Protection directive is: WordPressProtect [off|on|drop|deny|throttle|captcha, ] <limit>
The action is optional, and defaults to throttle. The limit can be set together with the action, and has a value of (0|1|2-1000)

Example:

NOTE: In order to use the captcha option, you need to configure the reCAPTCHA protection feature. Please see the How to Configure reCAPTCHA Protection guide for instructions.

This directive can be placed in the Apache configuration or .htaccess file.

The login limit value specifies the maximum number of wp-login.php and xmlrpc.php login attempts allowed within 5 minutes before the IP is blocked.

This limit is handled using a quota system that works as follows:

Examples

How to Enable LSWS WordPressProtect Feature on cPanel

As long as LSWS version is 5.2.3 or above, the LSWS WordPressProtect feature is enabled by default and does not need any extra configuration in the LSWS WebAdmin GUI or in Apache configurations. (WordPressProtect is disabled by default in LiteSpeed Web ADC.)

You may wish to override the default settings at the server level, virtual-host level or even the .htaccess level. Before making any changes, it helps to understand the logic that drives WordPressProtect at the different levels.

Changing the settings at the Apache-server-level configuration will override the setting for any Apache-based virtual host, but will have no impact on LSWS-native virtual hosts, which can only be controlled by LSWS-native settings.

Changing the settings at the Apache-virtual-host level configuration will override the server-level configuration as well as the .htaccess- level. This means that the server administrator's virtual host setting will override the end user's setting in .htaccess.

Let's look at some examples for a WHM/cpanel EA4 environment:

After you run the following, the WordPressProtect feature will be automatically enabled globally, and the limit will be set to 10 by default:

/usr/local/lsws/admin/misc/lsup.sh -f -v 5.2.3 (or above version)

You may wish to overide the default limit of 10 to another value such as 5. You will need to set it at the server level of the Apache configuration file:

vi /etc/apache2/conf.d/includes/pre_main_global.conf

and add:

<IfModule Litespeed>
WordPressProtect throttle, 5
</IfModule>

This will set the limit to 5 for all virtual hosts.

You can also disable the feature globally:

<IfModule Litespeed>
WordPressProtect off
</IfModule>

No matter how the server level is set, the end user has the ability to enable or disable it through .htaccess by adding the following:

<IfModule Litespeed>
WordPressProtect throttle, 15
</IfModule>

or

<IfModule Litespeed>
WordPressProtect throttle, 0
</IfModule>

However, the end user's preference does not override the virtual-host-level, if any setting is specified at that level. For example, if the feature is disabled in the virtual-host-level include file, then any directives in .htaccess will be ignored.

vi /etc/apache2/conf.d/userdata/std/2_4/$USER/example.com/wordpress.conf
<IfModule Litespeed>
WordPressProtect throttle, 0
</IfModule>

To verify the server and virtual host level settings, you may run the following command:

grep -i -r wordpressprotect * /etc/apache2/

The design logic looks like the following:

Server Level VHost Level .htaccess Result
not setnot setnot set10
5not setnot set5
5not set2020
510not set10
5102010

How to Enable LSWS WordPressProtect Feature on Plesk

Everything should be same as cPanel. The only difference is in where to place the directives.

Server-Level Configuration

Edit the file /usr/local/psa/admin/conf/templates/custom/domain/domainVirtualHost.php.

This file should be generated by the bash <(curl http://www.litespeedtech.com/packages/lscache/set_cache_root_policy.sh) script when you set up the cache root. If you haven't run it yet, please do so to enable cache root setup.

There are two blocks of the following code:

<IfModule Litespeed>
CacheRoot lscache
</IfModule>

We can insert the WordPressProtect code here, like so:

<IfModule Litespeed>
CacheRoot lscache
WordPressProtect throttle, 5
</IfModule>

This will override the default server-level setting from 10 to 5. Be sure to run /usr/local/psa/admin/sbin/httpdmng --reconfigure-all to regenerate the configuration file, then /usr/local/lsws/bin/lswsctrl restart to restart LSWS so the new setting takes effect.

Vhost-Level Setting

In the Plesk domain page, as seen in the screenshot, navigate to Apache & nginx Settings, add the following directive in Additional directives for HTTP and Additional directives for HTTPS, then click OK or Apply to save it:

<IfModule Litespeed>
WordPressProtect throttle, 5
</IfModule>

Plesk users may also use `.htaccess` to override the server-level setting.

Real Testing

Drop

This test was conducted with WordPressProtect set to drop 10. We can see the time start to increase at Round 6 and finally get a connection error at Round 10.

Round:  1
fail 0.291 status: 200
Round:  2
fail 0.256 status: 200
Round:  3
fail 0.256 status: 200
Round:  4
fail 0.279 status: 200
Round:  5
fail 0.249 status: 200
Round:  6
fail 1.275 status: 200
Round:  7
fail 2.287 status: 200
Round:  8
fail 3.267 status: 200
Round:  9
fail 4.271 status: 200
Round:  10
Erro MSG:  ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response',))

Deny

This test was conducted with WordPressProtect set to deny 10. We can see the time start to increase at Round 6 and start getting permission deny 403 status code from Round 10.

Round:  1
fail 0.292 status: 200
Round:  2
fail 0.267 status: 200
Round:  3
fail 0.270 status: 200
Round:  4
fail 0.253 status: 200
Round:  5
fail 0.268 status: 200
Round:  6
fail 1.257 status: 200
Round:  7
fail 2.276 status: 200
Round:  8
fail 3.260 status: 200
Round:  9
fail 4.182 status: 200
Round:  10
fail 1.010 status: 403

Log:

Brute force detected, deny

Throttle

This test was conducted with WordPressProtect set to throttle 10. We can see the time start to increase at Round 6 and start throttling from Round 10.

Round:  1
fail 0.289 status: 200
Round:  2
fail 0.269 status: 200
Round:  3
fail 0.268 status: 200
Round:  4
fail 0.243 status: 200
Round:  5
fail 0.263 status: 200
Round:  6
fail 1.269 status: 200
Round:  7
fail 2.266 status: 200
Round:  8
fail 3.280 status: 200
Round:  9
fail 4.182 status: 200
Round:  10
fail 29.249 status: 200

Log:

Brute force detected, throttle

Set "Trusted <ip>" in .htaccess to bypass the block and reCAPTCHA check

Since LSWS 5.4RC1, LSWS added virtual host trusted IP support, where you use Trusted 1.2.3.4, 5.6.7.8 for IPv4 or Trusted [2001:db8:85a3:8d3:1319:8a2e:370:7348] for IPv6 in Virtual Host document root .htaccess to unblock blocked IP and make that IP trusted for that vhost.

Troubleshooting

WordPress Protection Block Never Seems to Expire

Normally the WordPress protection block is expected to expire after 10 minutes, but a visitor is receiving constant 403 errors due to WordPress protection. The error log entry is as the following:

2018-11-06 15:41:30.862784 [NOTICE] [24.96.xxx.xxx] bot detected for vhost [APVH_kevinandamanda.com], reason: WordPressBruteForce, close connection!
2018-11-06 16:52:10.591124 [INFO] [108.162.237.188:58160] Client IP from header: 24.96.xxx.xxx, conn limit: 10000, cur conns: 13, access denied
2018-11-06 16:54:10.851797 [INFO] [108.162.*.*:57936] Client IP from header: 24.96.xxx.xxx, conn limit: 10000, cur conns: 13, access denied
2018-11-06 16:56:11.349033 [INFO] [108.162.*.*:57976] Client IP from header: 24.96.xxx.xxx, conn limit: 10000, cur conns: 13, access denied
2018-11-06 16:58:11.819620 [INFO] [108.162.*.*:58196] Client IP from header: 24.96.xxx.xxx, conn limit: 10000, cur conns: 13, access denied
2018-11-06 17:00:12.607042 [INFO] [108.162.*.*:58606] Client IP from header: 24.96.xxx.xxx, conn limit: 10000, cur conns: 13, access denied
2018-11-06 17:02:13.371969 [INFO] [108.162.*.*:56922] Client IP from header: 24.96.xxx.xxx, conn limit: 10000, cur conns: 13, access denied

The visitor has been blocked for a few hours, and the block is removed after restarting LSWS.

The explanation: WP protection blocking is only removed if the IP stops access attempts for a full 10 minutes. If the visitor constantly hits the server, the blocking won't be lifted. Restarting the web server will remove all IP blocks immediately.

The bot-detection bot detected or WordPressBruteForce only log when a drop action is set. There won't be log entries for the deny and throttle actions. It is designed this way because drop is a more serious action, which blocks further requests from that IP (treated as unwanted botnet) and the log is for robot detection.

2018-11-06 15:41:30.862784 [NOTICE] [24.96.xxx.xxx] bot detected for vhost [APVH_kevinandamanda.com], reason: WordPressBruteForce, close connection!

Bot detection is one-time logging, while deny and throttle are per request, and it could become annoying with many repeated log messages.