Version Description
- Removed the "Handle cookie login" setting as they are now obsolete.
- Added bruteforce protection against Woocommerce login page attacks. https://wordpress.org/support/topic/how-to-integrate-with-woocommerce-2/
- Added bruteforce protection against XMLRPC attacks. https://wordpress.org/support/topic/xmlrpc-7/
Download this release
Release Info
Developer | wpchefgadget |
Plugin | Limit Login Attempts Reloaded |
Version | 2.2.0 |
Comparing to | |
See all releases |
Code changes from version 2.1.0 to 2.2.0
- core/LimitLoginAttempts.php +96 -19
- limit-login-attempts-reloaded.php +1 -1
- readme.txt +9 -2
- views/options-page.php +1 -15
core/LimitLoginAttempts.php
CHANGED
@@ -60,29 +60,106 @@ class Limit_Login_Attempts {
|
|
60 |
*/
|
61 |
public function hooks_init() {
|
62 |
add_action( 'plugins_loaded', array($this, 'setup'), 9999 );
|
63 |
-
|
64 |
add_action( 'admin_enqueue_scripts', array($this, 'enqueue') );
|
|
|
65 |
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
|
|
|
|
70 |
|
71 |
-
|
|
|
|
|
72 |
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
78 |
}
|
79 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
add_filter( 'wp_authenticate_user', array($this, 'wp_authenticate_user'), 99999, 2 );
|
81 |
add_filter( 'shake_error_codes', array($this, 'failure_shake') );
|
82 |
add_action( 'login_head', array($this, 'add_error_message') );
|
83 |
add_action( 'login_errors', array($this, 'fixup_error_messages') );
|
84 |
add_action( 'admin_menu', array($this, 'admin_menu') );
|
85 |
|
|
|
|
|
|
|
|
|
|
|
|
|
86 |
/*
|
87 |
* This action should really be changed to the 'authenticate' filter as
|
88 |
* it will probably be deprecated. That is however only available in
|
@@ -91,13 +168,6 @@ class Limit_Login_Attempts {
|
|
91 |
add_action( 'wp_authenticate', array($this, 'track_credentials'), 10, 2 );
|
92 |
}
|
93 |
|
94 |
-
public function setup() {
|
95 |
-
$this->check_original_installed();
|
96 |
-
|
97 |
-
load_plugin_textdomain( 'limit-login-attempts-reloaded', false, plugin_basename( dirname( __FILE__ ) ) . '/../languages' );
|
98 |
-
$this->setup_options();
|
99 |
-
}
|
100 |
-
|
101 |
/**
|
102 |
* Check if the original plugin is installed
|
103 |
*/
|
@@ -402,6 +472,7 @@ class Limit_Login_Attempts {
|
|
402 |
* @param $username
|
403 |
*/
|
404 |
public function limit_login_failed( $username ) {
|
|
|
405 |
$ip = $this->get_address();
|
406 |
|
407 |
/* if currently locked-out, do not add to retries */
|
@@ -674,6 +745,7 @@ class Limit_Login_Attempts {
|
|
674 |
* @return \WP_Error
|
675 |
*/
|
676 |
public function wp_authenticate_user( $user, $password ) {
|
|
|
677 |
if( is_wp_error( $user ) || $this->is_limit_login_ok() ) {
|
678 |
return $user;
|
679 |
}
|
@@ -821,7 +893,8 @@ class Limit_Login_Attempts {
|
|
821 |
if( $limit_login_nonempty_credentials && $count > $my_warn_count ) {
|
822 |
/* Replace error message, including ours if necessary */
|
823 |
$content = __( '<strong>ERROR</strong>: Incorrect username or password.', 'limit-login-attempts-reloaded' ) . "<br />\n";
|
824 |
-
|
|
|
825 |
$content .= "<br />\n" . $this->get_message() . "<br />\n";
|
826 |
}
|
827 |
|
@@ -841,6 +914,10 @@ class Limit_Login_Attempts {
|
|
841 |
return $new;
|
842 |
}
|
843 |
|
|
|
|
|
|
|
|
|
844 |
/**
|
845 |
* Return current (error) message to show, if any
|
846 |
*
|
60 |
*/
|
61 |
public function hooks_init() {
|
62 |
add_action( 'plugins_loaded', array($this, 'setup'), 9999 );
|
|
|
63 |
add_action( 'admin_enqueue_scripts', array($this, 'enqueue') );
|
64 |
+
}
|
65 |
|
66 |
+
/**
|
67 |
+
* @param $error IXR_Error
|
68 |
+
*
|
69 |
+
* @return IXR_Error
|
70 |
+
*/
|
71 |
+
public function xmlrpc_error_messages( $error ) {
|
72 |
|
73 |
+
if( !class_exists( 'IXR_Error' ) ) {
|
74 |
+
return $error;
|
75 |
+
}
|
76 |
|
77 |
+
if( !$this->is_limit_login_ok() ) {
|
78 |
+
return new IXR_Error( 403, $this->error_msg() );
|
79 |
+
}
|
80 |
+
|
81 |
+
$ip = $this->get_address();
|
82 |
+
$retries = get_option( 'limit_login_retries' );
|
83 |
+
$valid = get_option( 'limit_login_retries_valid' );
|
84 |
+
|
85 |
+
/* Should we show retries remaining? */
|
86 |
+
|
87 |
+
if( !is_array( $retries ) || !is_array( $valid ) ) {
|
88 |
+
/* no retries at all */
|
89 |
+
return $error;
|
90 |
+
}
|
91 |
+
if( !isset( $retries[ $ip ] ) || !isset( $valid[ $ip ] ) || time() > $valid[ $ip ] ) {
|
92 |
+
/* no: no valid retries */
|
93 |
+
return $error;
|
94 |
+
}
|
95 |
+
if( ( $retries[ $ip ] % $this->get_option( 'allowed_retries' ) ) == 0 ) {
|
96 |
+
/* no: already been locked out for these retries */
|
97 |
+
return $error;
|
98 |
+
}
|
99 |
+
|
100 |
+
$remaining = max( ( $this->get_option( 'allowed_retries' ) - ( $retries[ $ip ] % $this->get_option( 'allowed_retries' ) ) ), 0 );
|
101 |
+
return new IXR_Error( 403, sprintf( _n( "<strong>%d</strong> attempt remaining.", "<strong>%d</strong> attempts remaining.", $remaining, 'limit-login-attempts-reloaded' ), $remaining ) );
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Errors on WooCommerce account page
|
106 |
+
*/
|
107 |
+
public function add_wc_notices() {
|
108 |
+
|
109 |
+
global $limit_login_just_lockedout, $limit_login_nonempty_credentials, $limit_login_my_error_shown;
|
110 |
+
|
111 |
+
if( !function_exists( 'is_account_page' ) || !function_exists( 'wc_add_notice' ) ) {
|
112 |
+
return;
|
113 |
+
}
|
114 |
+
|
115 |
+
/*
|
116 |
+
* During lockout we do not want to show any other error messages (like
|
117 |
+
* unknown user or empty password).
|
118 |
+
*/
|
119 |
+
if( empty( $_POST ) && !$this->is_limit_login_ok() && !$limit_login_just_lockedout ) {
|
120 |
+
if( is_account_page() ) {
|
121 |
+
wc_add_notice( $this->error_msg(), 'error' );
|
122 |
}
|
123 |
}
|
124 |
+
|
125 |
+
}
|
126 |
+
|
127 |
+
public function setup() {
|
128 |
+
$this->check_original_installed();
|
129 |
+
|
130 |
+
load_plugin_textdomain( 'limit-login-attempts-reloaded', false, plugin_basename( dirname( __FILE__ ) ) . '/../languages' );
|
131 |
+
$this->setup_options();
|
132 |
+
|
133 |
+
add_action( 'wp_login_failed', array($this, 'limit_login_failed') );
|
134 |
+
|
135 |
+
// TODO: remove this
|
136 |
+
// if( $this->get_option( 'cookies' ) ) {
|
137 |
+
// $this->handle_cookies();
|
138 |
+
//
|
139 |
+
// add_action( 'auth_cookie_bad_username', array($this, 'failed_cookie') );
|
140 |
+
//
|
141 |
+
// global $wp_version;
|
142 |
+
//
|
143 |
+
// if( version_compare( $wp_version, '3.0', '>=' ) ) {
|
144 |
+
// add_action( 'auth_cookie_bad_hash', array($this, 'failed_cookie_hash') );
|
145 |
+
// add_action( 'auth_cookie_valid', array($this, 'valid_cookie'), 10, 2 );
|
146 |
+
// } else {
|
147 |
+
// add_action( 'auth_cookie_bad_hash', array($this, 'failed_cookie') );
|
148 |
+
// }
|
149 |
+
// }
|
150 |
+
|
151 |
add_filter( 'wp_authenticate_user', array($this, 'wp_authenticate_user'), 99999, 2 );
|
152 |
add_filter( 'shake_error_codes', array($this, 'failure_shake') );
|
153 |
add_action( 'login_head', array($this, 'add_error_message') );
|
154 |
add_action( 'login_errors', array($this, 'fixup_error_messages') );
|
155 |
add_action( 'admin_menu', array($this, 'admin_menu') );
|
156 |
|
157 |
+
// Add notices for XMLRPC request
|
158 |
+
add_filter( 'xmlrpc_login_error', array($this, 'xmlrpc_error_messages') );
|
159 |
+
|
160 |
+
// Add notices to woocommerce login page
|
161 |
+
add_action( 'wp_head', array($this, 'add_wc_notices') );
|
162 |
+
|
163 |
/*
|
164 |
* This action should really be changed to the 'authenticate' filter as
|
165 |
* it will probably be deprecated. That is however only available in
|
168 |
add_action( 'wp_authenticate', array($this, 'track_credentials'), 10, 2 );
|
169 |
}
|
170 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
171 |
/**
|
172 |
* Check if the original plugin is installed
|
173 |
*/
|
472 |
* @param $username
|
473 |
*/
|
474 |
public function limit_login_failed( $username ) {
|
475 |
+
|
476 |
$ip = $this->get_address();
|
477 |
|
478 |
/* if currently locked-out, do not add to retries */
|
745 |
* @return \WP_Error
|
746 |
*/
|
747 |
public function wp_authenticate_user( $user, $password ) {
|
748 |
+
|
749 |
if( is_wp_error( $user ) || $this->is_limit_login_ok() ) {
|
750 |
return $user;
|
751 |
}
|
893 |
if( $limit_login_nonempty_credentials && $count > $my_warn_count ) {
|
894 |
/* Replace error message, including ours if necessary */
|
895 |
$content = __( '<strong>ERROR</strong>: Incorrect username or password.', 'limit-login-attempts-reloaded' ) . "<br />\n";
|
896 |
+
|
897 |
+
if( $limit_login_my_error_shown || $this->get_message() ) {
|
898 |
$content .= "<br />\n" . $this->get_message() . "<br />\n";
|
899 |
}
|
900 |
|
914 |
return $new;
|
915 |
}
|
916 |
|
917 |
+
public function fixup_error_messages_wc( \WP_Error $error ) {
|
918 |
+
$error->add( 1, __( 'WC Error' ) );
|
919 |
+
}
|
920 |
+
|
921 |
/**
|
922 |
* Return current (error) message to show, if any
|
923 |
*
|
limit-login-attempts-reloaded.php
CHANGED
@@ -4,7 +4,7 @@
|
|
4 |
Description: Limit the rate of login attempts, including by way of cookies and for each IP address.
|
5 |
Author: wpchefgadget
|
6 |
Text Domain: limit-login-attempts-reloaded
|
7 |
-
Version: 2.
|
8 |
|
9 |
Copyright 2008 - 2012 Johan Eenfeldt
|
10 |
|
4 |
Description: Limit the rate of login attempts, including by way of cookies and for each IP address.
|
5 |
Author: wpchefgadget
|
6 |
Text Domain: limit-login-attempts-reloaded
|
7 |
+
Version: 2.2.0
|
8 |
|
9 |
Copyright 2008 - 2012 Johan Eenfeldt
|
10 |
|
readme.txt
CHANGED
@@ -2,8 +2,8 @@
|
|
2 |
Contributors: wpchefgadget
|
3 |
Tags: login, security, authentication, Limit Login Attempts, Limit Login Attempts Reloaded, Limit Login Attempts Revamped, Limit Login Attempts Renovated, Limit Login Attempts Updated, Better Limit Login Attempts, Limit Login Attempts Renewed, Limit Login Attempts Upgraded
|
4 |
Requires at least: 2.8
|
5 |
-
Tested up to: 4.6
|
6 |
-
Stable tag: 2.
|
7 |
|
8 |
Reloaded version of the original Limit Login Attempts plugin for Login Protection by a team of WordPress developers.
|
9 |
|
@@ -21,6 +21,8 @@ Features:
|
|
21 |
* Optional logging and optional email notification.
|
22 |
* Handles server behind the reverse proxy.
|
23 |
* It is possible to whitelist IPs using a filter. But you probably shouldn't do this.
|
|
|
|
|
24 |
|
25 |
= Upgrading from the old Limit Login Attempts plugin =
|
26 |
1. Go to the Plugins section in your site's backend.
|
@@ -46,6 +48,11 @@ Based on the original code from Limit Login Attemps plugin by Johan Eenfeldt.
|
|
46 |
|
47 |
== Changelog ==
|
48 |
|
|
|
|
|
|
|
|
|
|
|
49 |
= 2.1.0 =
|
50 |
* The site connection settings are now applied automatically and therefore have been removed from the admin interface.
|
51 |
* Now compatible with PHP 5.2 to support some older WP installations.
|
2 |
Contributors: wpchefgadget
|
3 |
Tags: login, security, authentication, Limit Login Attempts, Limit Login Attempts Reloaded, Limit Login Attempts Revamped, Limit Login Attempts Renovated, Limit Login Attempts Updated, Better Limit Login Attempts, Limit Login Attempts Renewed, Limit Login Attempts Upgraded
|
4 |
Requires at least: 2.8
|
5 |
+
Tested up to: 4.6.1
|
6 |
+
Stable tag: 2.2.0
|
7 |
|
8 |
Reloaded version of the original Limit Login Attempts plugin for Login Protection by a team of WordPress developers.
|
9 |
|
21 |
* Optional logging and optional email notification.
|
22 |
* Handles server behind the reverse proxy.
|
23 |
* It is possible to whitelist IPs using a filter. But you probably shouldn't do this.
|
24 |
+
* XMLRPC gateway protection.
|
25 |
+
* Woocommerce login page protection.
|
26 |
|
27 |
= Upgrading from the old Limit Login Attempts plugin =
|
28 |
1. Go to the Plugins section in your site's backend.
|
48 |
|
49 |
== Changelog ==
|
50 |
|
51 |
+
= 2.2.0 =
|
52 |
+
* Removed the "Handle cookie login" setting as they are now obsolete.
|
53 |
+
* Added bruteforce protection against Woocommerce login page attacks. https://wordpress.org/support/topic/how-to-integrate-with-woocommerce-2/
|
54 |
+
* Added bruteforce protection against XMLRPC attacks. https://wordpress.org/support/topic/xmlrpc-7/
|
55 |
+
|
56 |
= 2.1.0 =
|
57 |
* The site connection settings are now applied automatically and therefore have been removed from the admin interface.
|
58 |
* Now compatible with PHP 5.2 to support some older WP installations.
|
views/options-page.php
CHANGED
@@ -44,7 +44,6 @@ if( isset( $_POST[ 'update_options' ] ) ) {
|
|
44 |
$this->_options[ 'allowed_lockouts' ] = $_POST[ 'allowed_lockouts' ];
|
45 |
$this->_options[ 'long_duration' ] = $_POST[ 'long_duration' ] * 3600;
|
46 |
$this->_options[ 'notify_email_after' ] = $_POST[ 'email_after' ];
|
47 |
-
$this->_options[ 'cookies' ] = ( isset( $_POST[ 'cookies' ] ) && $_POST[ 'cookies' ] == '1' );
|
48 |
|
49 |
$notify_methods = array();
|
50 |
if( isset( $_POST[ 'lockout_notify_log' ] ) ) {
|
@@ -65,9 +64,6 @@ $lockouts_total = get_option( 'limit_login_lockouts_total', 0 );
|
|
65 |
$lockouts = get_option( 'limit_login_lockouts' );
|
66 |
$lockouts_now = is_array( $lockouts ) ? count( $lockouts ) : 0;
|
67 |
|
68 |
-
$cookies_yes = $this->get_option( 'cookies' ) ? ' checked ' : '';
|
69 |
-
$cookies_no = $this->get_option( 'cookies' ) ? '' : ' checked ';
|
70 |
-
|
71 |
$v = explode( ',', $this->get_option( 'lockout_notify' ) );
|
72 |
$log_checked = in_array( 'log', $v ) ? ' checked ' : '';
|
73 |
$email_checked = in_array( 'email', $v ) ? ' checked ' : '';
|
@@ -96,7 +92,7 @@ $email_checked = in_array( 'email', $v ) ? ' checked ' : '';
|
|
96 |
<th scope="row"
|
97 |
valign="top"><?php echo __( 'Active lockouts', 'limit-login-attempts-reloaded' ); ?></th>
|
98 |
<td>
|
99 |
-
<input name="reset_current"
|
100 |
value="<?php echo __( 'Restore Lockouts', 'limit-login-attempts-reloaded' ); ?>"
|
101 |
type="submit"/>
|
102 |
<?php echo sprintf( __( '%d IP is currently blocked from trying to log in', 'limit-login-attempts-reloaded' ), $lockouts_now ); ?>
|
@@ -131,16 +127,6 @@ $email_checked = in_array( 'email', $v ) ? ' checked ' : '';
|
|
131 |
name="valid_duration"/> <?php echo __( 'hours until retries are reset', 'limit-login-attempts-reloaded' ); ?>
|
132 |
</td>
|
133 |
</tr>
|
134 |
-
<tr>
|
135 |
-
<th scope="row"
|
136 |
-
valign="top"><?php echo __( 'Handle cookie login', 'limit-login-attempts-reloaded' ); ?></th>
|
137 |
-
<td>
|
138 |
-
<label><input type="radio" name="cookies" <?php echo $cookies_yes; ?>
|
139 |
-
value="1"/> <?php echo __( 'Yes', 'limit-login-attempts-reloaded' ); ?></label>
|
140 |
-
<label><input type="radio" name="cookies" <?php echo $cookies_no; ?>
|
141 |
-
value="0"/> <?php echo __( 'No', 'limit-login-attempts-reloaded' ); ?></label>
|
142 |
-
</td>
|
143 |
-
</tr>
|
144 |
<tr>
|
145 |
<th scope="row"
|
146 |
valign="top"><?php echo __( 'Notify on lockout', 'limit-login-attempts-reloaded' ); ?></th>
|
44 |
$this->_options[ 'allowed_lockouts' ] = $_POST[ 'allowed_lockouts' ];
|
45 |
$this->_options[ 'long_duration' ] = $_POST[ 'long_duration' ] * 3600;
|
46 |
$this->_options[ 'notify_email_after' ] = $_POST[ 'email_after' ];
|
|
|
47 |
|
48 |
$notify_methods = array();
|
49 |
if( isset( $_POST[ 'lockout_notify_log' ] ) ) {
|
64 |
$lockouts = get_option( 'limit_login_lockouts' );
|
65 |
$lockouts_now = is_array( $lockouts ) ? count( $lockouts ) : 0;
|
66 |
|
|
|
|
|
|
|
67 |
$v = explode( ',', $this->get_option( 'lockout_notify' ) );
|
68 |
$log_checked = in_array( 'log', $v ) ? ' checked ' : '';
|
69 |
$email_checked = in_array( 'email', $v ) ? ' checked ' : '';
|
92 |
<th scope="row"
|
93 |
valign="top"><?php echo __( 'Active lockouts', 'limit-login-attempts-reloaded' ); ?></th>
|
94 |
<td>
|
95 |
+
<input class="button" name="reset_current"
|
96 |
value="<?php echo __( 'Restore Lockouts', 'limit-login-attempts-reloaded' ); ?>"
|
97 |
type="submit"/>
|
98 |
<?php echo sprintf( __( '%d IP is currently blocked from trying to log in', 'limit-login-attempts-reloaded' ), $lockouts_now ); ?>
|
127 |
name="valid_duration"/> <?php echo __( 'hours until retries are reset', 'limit-login-attempts-reloaded' ); ?>
|
128 |
</td>
|
129 |
</tr>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
130 |
<tr>
|
131 |
<th scope="row"
|
132 |
valign="top"><?php echo __( 'Notify on lockout', 'limit-login-attempts-reloaded' ); ?></th>
|