Version Description
Download this release
Release Info
Developer | mvandemar |
Plugin | Login LockDown |
Version | 1.6 |
Comparing to | |
See all releases |
Code changes from version 1.5 to 1.6
- loginlockdown.php +179 -121
- readme.txt +45 -4
- version.txt +1 -1
loginlockdown.php
CHANGED
@@ -2,24 +2,31 @@
|
|
2 |
/*
|
3 |
Plugin Name: Login LockDown
|
4 |
Plugin URI: http://www.bad-neighborhood.com/
|
5 |
-
Version: v1.
|
6 |
Author: Michael VanDeMar
|
7 |
Description: Adds some extra security to WordPress by restricting the rate at which failed logins can be re-attempted from a given IP range. Distributed through <a href="http://www.bad-neighborhood.com/" target="_blank">Bad Neighborhood</a>.
|
8 |
*/
|
9 |
|
10 |
/*
|
11 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
*
|
13 |
* ver. 1.5 17-Sep-2009
|
14 |
* - implemented wp_nonce security in the options and lockdown release forms in the admin screen
|
15 |
-
* - fixed a security hole with an improperly escaped SQL query
|
16 |
-
* - encoded certain outputs in the admin panel using esc_attr() to prevent XSS attacks
|
17 |
-
* - fixed an issue with the 'Lockout Invalid Usernames' option not functioning as intended
|
18 |
*
|
19 |
* ver. 1.4 29-Aug-2009
|
20 |
* - removed erroneous error affecting WP 2.8+
|
21 |
-
* - fixed activation error caused by customizing the location of the wp-content folder
|
22 |
-
* - added in the option to mask which specific login error (invalid username or invalid password) was generated
|
23 |
* - added in the option to lock out failed login attempts even if the username doesn't exist
|
24 |
*
|
25 |
* ver. 1.3 23-Feb-2009
|
@@ -52,7 +59,7 @@ Description: Adds some extra security to WordPress by restricting the rate at wh
|
|
52 |
| |
|
53 |
| Login LockDown - added security measures to WordPress intended to |
|
54 |
| inhibit or reduce brute force password discovery. |
|
55 |
-
| Copyright (C) 2007 -
|
56 |
| http://www.bad-neighborhood.com |
|
57 |
| All rights reserved. |
|
58 |
| |
|
@@ -80,6 +87,7 @@ $loginlockdownOptions = get_loginlockdownOptions();
|
|
80 |
|
81 |
function loginLockdown_install() {
|
82 |
global $wpdb;
|
|
|
83 |
$table_name = $wpdb->prefix . "login_fails";
|
84 |
|
85 |
if( $wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name ) {
|
@@ -91,9 +99,8 @@ function loginLockdown_install() {
|
|
91 |
PRIMARY KEY (`login_attempt_ID`)
|
92 |
);";
|
93 |
|
94 |
-
require_once(ABSPATH . 'wp-admin/upgrade
|
95 |
dbDelta($sql);
|
96 |
-
add_option("loginlockdown_db1_version", $loginlockdown_db_version);
|
97 |
}
|
98 |
|
99 |
$table_name = $wpdb->prefix . "lockdowns";
|
@@ -108,10 +115,13 @@ function loginLockdown_install() {
|
|
108 |
PRIMARY KEY (`lockdown_ID`)
|
109 |
);";
|
110 |
|
111 |
-
require_once(ABSPATH . 'wp-admin/upgrade
|
112 |
dbDelta($sql);
|
113 |
-
add_option("loginlockdown_db2_version", $loginlockdown_db_version);
|
114 |
}
|
|
|
|
|
|
|
|
|
115 |
}
|
116 |
|
117 |
function countFails($username = "") {
|
@@ -121,24 +131,33 @@ function countFails($username = "") {
|
|
121 |
$ip = $_SERVER['REMOTE_ADDR'];
|
122 |
$class_c = substr ($ip, 0 , strrpos ( $ip, "." ));
|
123 |
|
124 |
-
$
|
125 |
"WHERE login_attempt_date + INTERVAL " .
|
126 |
$loginlockdownOptions['retries_within'] . " MINUTE > now() AND " .
|
127 |
-
"login_attempt_IP LIKE '
|
|
|
|
|
|
|
128 |
return $numFails;
|
129 |
}
|
130 |
|
131 |
function incrementFails($username = "") {
|
132 |
-
global $wpdb;
|
133 |
global $loginlockdownOptions;
|
134 |
$table_name = $wpdb->prefix . "login_fails";
|
135 |
$ip = $_SERVER['REMOTE_ADDR'];
|
136 |
|
137 |
$username = sanitize_user($username);
|
138 |
-
$user =
|
139 |
if ( $user || "yes" == $loginlockdownOptions['lockout_invalid_usernames'] ) {
|
|
|
|
|
|
|
|
|
|
|
140 |
$insert = "INSERT INTO " . $table_name . " (user_id, login_attempt_date, login_attempt_IP) " .
|
141 |
-
"VALUES ('" . $
|
|
|
142 |
$results = $wpdb->query($insert);
|
143 |
}
|
144 |
}
|
@@ -150,11 +169,17 @@ function lockDown($username = "") {
|
|
150 |
$ip = $_SERVER['REMOTE_ADDR'];
|
151 |
|
152 |
$username = sanitize_user($username);
|
153 |
-
$user =
|
154 |
if ( $user || "yes" == $loginlockdownOptions['lockout_invalid_usernames'] ) {
|
|
|
|
|
|
|
|
|
|
|
155 |
$insert = "INSERT INTO " . $table_name . " (user_id, lockdown_date, release_date, lockdown_IP) " .
|
156 |
-
"VALUES ('" . $
|
157 |
-
$loginlockdownOptions['lockout_length'] . " MINUTE), '
|
|
|
158 |
$results = $wpdb->query($insert);
|
159 |
}
|
160 |
}
|
@@ -165,9 +190,12 @@ function isLockedDown() {
|
|
165 |
$ip = $_SERVER['REMOTE_ADDR'];
|
166 |
$class_c = substr ($ip, 0 , strrpos ( $ip, "." ));
|
167 |
|
168 |
-
$
|
169 |
"WHERE release_date > now() AND " .
|
170 |
-
"lockdown_IP LIKE
|
|
|
|
|
|
|
171 |
|
172 |
return $stillLocked;
|
173 |
}
|
@@ -186,9 +214,11 @@ function get_loginlockdownOptions() {
|
|
186 |
$loginlockdownAdminOptions = array(
|
187 |
'max_login_retries' => 3,
|
188 |
'retries_within' => 5,
|
189 |
-
'lockout_length' => 60,
|
190 |
-
'lockout_invalid_usernames' => 'no',
|
191 |
-
'mask_login_errors' => 'no'
|
|
|
|
|
192 |
$loginlockdownOptions = get_option("loginlockdownAdminOptions");
|
193 |
if ( !empty($loginlockdownOptions) ) {
|
194 |
foreach ( $loginlockdownOptions as $key => $option ) {
|
@@ -202,13 +232,13 @@ function get_loginlockdownOptions() {
|
|
202 |
function print_loginlockdownAdminPage() {
|
203 |
global $wpdb;
|
204 |
$table_name = $wpdb->prefix . "lockdowns";
|
205 |
-
$loginlockdownAdminOptions = get_loginlockdownOptions();
|
206 |
-
|
207 |
if (isset($_POST['update_loginlockdownSettings'])) {
|
208 |
-
|
209 |
-
//wp_nonce check
|
210 |
-
check_admin_referer('login-lockdown_update-options');
|
211 |
-
|
212 |
if (isset($_POST['ll_max_login_retries'])) {
|
213 |
$loginlockdownAdminOptions['max_login_retries'] = $_POST['ll_max_login_retries'];
|
214 |
}
|
@@ -224,21 +254,26 @@ function print_loginlockdownAdminPage() {
|
|
224 |
if (isset($_POST['ll_mask_login_errors'])) {
|
225 |
$loginlockdownAdminOptions['mask_login_errors'] = $_POST['ll_mask_login_errors'];
|
226 |
}
|
|
|
|
|
|
|
227 |
update_option("loginlockdownAdminOptions", $loginlockdownAdminOptions);
|
228 |
?>
|
229 |
<div class="updated"><p><strong><?php _e("Settings Updated.", "loginlockdown");?></strong></p></div>
|
230 |
<?php
|
231 |
}
|
232 |
-
if (isset($_POST['release_lockdowns'])) {
|
233 |
-
|
234 |
-
//wp_nonce check
|
235 |
-
check_admin_referer('login-lockdown_release-lockdowns');
|
236 |
|
237 |
if (isset($_POST['releaseme'])) {
|
238 |
$released = $_POST['releaseme'];
|
239 |
foreach ( $released as $release_id ) {
|
240 |
-
$
|
241 |
-
"WHERE lockdown_ID = "
|
|
|
|
|
242 |
}
|
243 |
}
|
244 |
update_option("loginlockdownAdminOptions", $loginlockdownAdminOptions);
|
@@ -248,37 +283,50 @@ function print_loginlockdownAdminPage() {
|
|
248 |
}
|
249 |
$dalist = listLockedDown();
|
250 |
?>
|
251 |
-
<div class=wrap>
|
252 |
-
<form method="post" action="<?php echo esc_attr($_SERVER["REQUEST_URI"]); ?>">
|
253 |
-
<?php
|
254 |
-
if ( function_exists('wp_nonce_field') )
|
255 |
-
wp_nonce_field('login-lockdown_update-options');
|
256 |
?>
|
257 |
<h2><?php _e('Login LockDown Options', 'loginlockdown') ?></h2>
|
258 |
<h3><?php _e('Max Login Retries', 'loginlockdown') ?></h3>
|
259 |
-
<
|
|
|
260 |
<h3><?php _e('Retry Time Period Restriction (minutes)', 'loginlockdown') ?></h3>
|
261 |
-
<
|
|
|
262 |
<h3><?php _e('Lockout Length (minutes)', 'loginlockdown') ?></h3>
|
263 |
-
<
|
|
|
264 |
<h3><?php _e('Lockout Invalid Usernames?', 'loginlockdown') ?></h3>
|
265 |
-
<
|
|
|
266 |
<h3><?php _e('Mask Login Errors?', 'loginlockdown') ?></h3>
|
267 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
268 |
<div class="submit">
|
269 |
-
<input type="submit" name="update_loginlockdownSettings" value="<?php _e('Update Settings', 'loginlockdown') ?>" /></div>
|
270 |
</form>
|
271 |
<br />
|
272 |
-
<form method="post" action="<?php echo esc_attr($_SERVER["REQUEST_URI"]); ?>">
|
273 |
-
<?php
|
274 |
-
if ( function_exists('wp_nonce_field') )
|
275 |
-
wp_nonce_field('login-lockdown_release-lockdowns');
|
276 |
?>
|
277 |
<h3><?php _e('Currently Locked Out', 'loginlockdown') ?></h3>
|
278 |
<?php
|
279 |
$num_lockedout = count($dalist);
|
280 |
if( 0 == $num_lockedout ) {
|
281 |
-
echo "<p>No
|
282 |
} else {
|
283 |
foreach ( $dalist as $key => $option ) {
|
284 |
?>
|
@@ -288,7 +336,7 @@ if ( function_exists('wp_nonce_field') )
|
|
288 |
}
|
289 |
?>
|
290 |
<div class="submit">
|
291 |
-
<input type="submit" name="release_lockdowns" value="<?php _e('Release Selected', 'loginlockdown') ?>" /></div>
|
292 |
</form>
|
293 |
</div>
|
294 |
<?php
|
@@ -296,105 +344,115 @@ if ( function_exists('wp_nonce_field') )
|
|
296 |
|
297 |
function loginlockdown_ap() {
|
298 |
if ( function_exists('add_options_page') ) {
|
299 |
-
add_options_page('Login LockDown', 'Login LockDown',
|
300 |
}
|
301 |
}
|
302 |
|
303 |
function ll_credit_link(){
|
304 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
305 |
}
|
306 |
|
307 |
//Actions and Filters
|
308 |
if ( isset($loginlockdown_db_version) ) {
|
309 |
//Actions
|
310 |
add_action('admin_menu', 'loginlockdown_ap');
|
311 |
-
if(!defined('WP_PLUGIN_DIR')){
|
312 |
define('WP_PLUGIN_DIR', ABSPATH . 'wp-content/plugins');
|
313 |
}
|
314 |
-
$activatestr = str_replace(WP_PLUGIN_DIR . "/", "activate_", __FILE__);
|
315 |
add_action($activatestr, 'loginLockdown_install');
|
316 |
-
add_action('login_form', 'll_credit_link');
|
317 |
-
|
318 |
-
remove_filter('authenticate', 'wp_authenticate_username_password', 20, 3);
|
319 |
add_filter('authenticate', 'll_wp_authenticate_username_password', 20, 3);
|
320 |
//Filters
|
321 |
-
//Functions
|
322 |
-
function ll_wp_authenticate_username_password($user, $username, $password) {
|
323 |
-
if ( is_a($user, 'WP_User') ) { return $user; }
|
324 |
-
|
325 |
-
if ( empty($username) || empty($password) ) {
|
326 |
-
$error = new WP_Error();
|
327 |
-
|
328 |
-
if ( empty($username) )
|
329 |
-
$error->add('empty_username', __('<strong>ERROR</strong>: The username field is empty.'));
|
330 |
-
|
331 |
-
if ( empty($password) )
|
332 |
-
$error->add('empty_password', __('<strong>ERROR</strong>: The password field is empty.'));
|
333 |
-
|
334 |
-
return $error;
|
335 |
-
}
|
336 |
-
|
337 |
-
$userdata =
|
338 |
-
|
339 |
-
if ( !$userdata ) {
|
340 |
-
return new WP_Error('invalid_username', sprintf(__('<strong>ERROR</strong>: Invalid username. <a href="%s" title="Password Lost and Found">Lost your password</a>?'), site_url('wp-login.php?action=lostpassword', 'login')));
|
341 |
-
}
|
342 |
-
|
343 |
-
$userdata = apply_filters('wp_authenticate_user', $userdata, $password);
|
344 |
-
if ( is_wp_error($userdata) ) {
|
345 |
-
return $userdata;
|
346 |
-
}
|
347 |
-
|
348 |
-
if ( !wp_check_password($password, $userdata->user_pass, $userdata->ID) ) {
|
349 |
-
return new WP_Error('incorrect_password', sprintf(__('<strong>ERROR</strong>: Incorrect password. <a href="%s" title="Password Lost and Found">Lost your password</a>?'), site_url('wp-login.php?action=lostpassword', 'login')));
|
350 |
-
}
|
351 |
-
|
352 |
-
$user = new WP_User($userdata->ID);
|
353 |
-
return $user;
|
354 |
-
}
|
355 |
-
|
356 |
|
357 |
if ( !function_exists('wp_authenticate') ) :
|
358 |
function wp_authenticate($username, $password) {
|
359 |
global $wpdb, $error;
|
360 |
-
global $loginlockdownOptions;
|
361 |
-
|
362 |
-
$username = sanitize_user($username);
|
363 |
-
$password = trim($password);
|
364 |
-
|
365 |
if ( "" != isLockedDown() ) {
|
366 |
return new WP_Error('incorrect_password', "<strong>ERROR</strong>: We're sorry, but this IP range has been blocked due to too many recent " .
|
367 |
"failed login attempts.<br /><br />Please try again later.");
|
368 |
}
|
369 |
-
|
370 |
-
$user = apply_filters('authenticate', null, $username, $password);
|
371 |
-
|
372 |
-
if ( $user == null ) {
|
373 |
-
// TODO what should the error message be? (Or would these even happen?)
|
374 |
-
// Only needed if all authentication handlers fail to return anything.
|
375 |
-
$user = new WP_Error('authentication_failed', __('<strong>ERROR</strong>: Invalid username or incorrect password.'));
|
376 |
-
}
|
377 |
-
|
378 |
-
$ignore_codes = array('empty_username', 'empty_password');
|
379 |
-
|
380 |
-
if (is_wp_error($user) && !in_array($user->get_error_code(), $ignore_codes) ) {
|
381 |
incrementFails($username);
|
382 |
if ( $loginlockdownOptions['max_login_retries'] <= countFails($username) ) {
|
383 |
lockDown($username);
|
384 |
return new WP_Error('incorrect_password', __("<strong>ERROR</strong>: We're sorry, but this IP range has been blocked due to too many recent " .
|
385 |
"failed login attempts.<br /><br />Please try again later."));
|
386 |
-
}
|
387 |
-
if ( 'yes' == $loginlockdownOptions['mask_login_errors'] ) {
|
388 |
-
return new WP_Error('authentication_failed', sprintf(__('<strong>ERROR</strong>: Invalid username or incorrect password. <a href="%s" title="Password Lost and Found">Lost your password</a>?'), site_url('wp-login.php?action=lostpassword', 'login')));
|
389 |
} else {
|
390 |
-
do_action('wp_login_failed', $username);
|
391 |
-
}
|
392 |
-
}
|
393 |
-
|
394 |
return $user;
|
395 |
}
|
396 |
endif;
|
397 |
}
|
398 |
|
399 |
|
400 |
-
?>
|
2 |
/*
|
3 |
Plugin Name: Login LockDown
|
4 |
Plugin URI: http://www.bad-neighborhood.com/
|
5 |
+
Version: v1.6
|
6 |
Author: Michael VanDeMar
|
7 |
Description: Adds some extra security to WordPress by restricting the rate at which failed logins can be re-attempted from a given IP range. Distributed through <a href="http://www.bad-neighborhood.com/" target="_blank">Bad Neighborhood</a>.
|
8 |
*/
|
9 |
|
10 |
/*
|
11 |
+
== Change Log ==
|
12 |
+
*
|
13 |
+
* ver. 1.6 7-Mar-2014
|
14 |
+
* - cleaned up deprecated functions
|
15 |
+
* - fixed bug with invalid property on a non-object when locking out invalid usernames
|
16 |
+
* - fixed utilization of $wpdb->prepare
|
17 |
+
* - added more descriptive help text to each of the options
|
18 |
+
* - added the ability to remove the "Login form protected by Login LockDown." message from within the dashboard
|
19 |
*
|
20 |
* ver. 1.5 17-Sep-2009
|
21 |
* - implemented wp_nonce security in the options and lockdown release forms in the admin screen
|
22 |
+
* - fixed a security hole with an improperly escaped SQL query
|
23 |
+
* - encoded certain outputs in the admin panel using esc_attr() to prevent XSS attacks
|
24 |
+
* - fixed an issue with the 'Lockout Invalid Usernames' option not functioning as intended
|
25 |
*
|
26 |
* ver. 1.4 29-Aug-2009
|
27 |
* - removed erroneous error affecting WP 2.8+
|
28 |
+
* - fixed activation error caused by customizing the location of the wp-content folder
|
29 |
+
* - added in the option to mask which specific login error (invalid username or invalid password) was generated
|
30 |
* - added in the option to lock out failed login attempts even if the username doesn't exist
|
31 |
*
|
32 |
* ver. 1.3 23-Feb-2009
|
59 |
| |
|
60 |
| Login LockDown - added security measures to WordPress intended to |
|
61 |
| inhibit or reduce brute force password discovery. |
|
62 |
+
| Copyright (C) 2007 - 2014, Michael VanDeMar, |
|
63 |
| http://www.bad-neighborhood.com |
|
64 |
| All rights reserved. |
|
65 |
| |
|
87 |
|
88 |
function loginLockdown_install() {
|
89 |
global $wpdb;
|
90 |
+
global $loginlockdown_db_version;
|
91 |
$table_name = $wpdb->prefix . "login_fails";
|
92 |
|
93 |
if( $wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name ) {
|
99 |
PRIMARY KEY (`login_attempt_ID`)
|
100 |
);";
|
101 |
|
102 |
+
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
103 |
dbDelta($sql);
|
|
|
104 |
}
|
105 |
|
106 |
$table_name = $wpdb->prefix . "lockdowns";
|
115 |
PRIMARY KEY (`lockdown_ID`)
|
116 |
);";
|
117 |
|
118 |
+
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
119 |
dbDelta($sql);
|
|
|
120 |
}
|
121 |
+
add_option("loginlockdown_db_version", "1.0", "", "no");
|
122 |
+
// added in 1.6, cleanup from previously improperly set db versions
|
123 |
+
delete_option( "loginlockdown_db1_version" );
|
124 |
+
delete_option( "loginlockdown_db2_version" );
|
125 |
}
|
126 |
|
127 |
function countFails($username = "") {
|
131 |
$ip = $_SERVER['REMOTE_ADDR'];
|
132 |
$class_c = substr ($ip, 0 , strrpos ( $ip, "." ));
|
133 |
|
134 |
+
$numFailsquery = "SELECT COUNT(login_attempt_ID) FROM $table_name " .
|
135 |
"WHERE login_attempt_date + INTERVAL " .
|
136 |
$loginlockdownOptions['retries_within'] . " MINUTE > now() AND " .
|
137 |
+
"login_attempt_IP LIKE '%s'";
|
138 |
+
$numFailsquery = $wpdb->prepare( $numFailsquery, $class_c . "%");
|
139 |
+
|
140 |
+
$numFails = $wpdb->get_var($numFailsquery);
|
141 |
return $numFails;
|
142 |
}
|
143 |
|
144 |
function incrementFails($username = "") {
|
145 |
+
global $wpdb;
|
146 |
global $loginlockdownOptions;
|
147 |
$table_name = $wpdb->prefix . "login_fails";
|
148 |
$ip = $_SERVER['REMOTE_ADDR'];
|
149 |
|
150 |
$username = sanitize_user($username);
|
151 |
+
$user = get_user_by('login',$username);
|
152 |
if ( $user || "yes" == $loginlockdownOptions['lockout_invalid_usernames'] ) {
|
153 |
+
if ( $user === false ) {
|
154 |
+
$user_id = -1;
|
155 |
+
} else {
|
156 |
+
$user_id = $user->ID;
|
157 |
+
}
|
158 |
$insert = "INSERT INTO " . $table_name . " (user_id, login_attempt_date, login_attempt_IP) " .
|
159 |
+
"VALUES ('" . $user_id . "', now(), '%s')";
|
160 |
+
$insert = $wpdb->prepare( $insert, $ip );
|
161 |
$results = $wpdb->query($insert);
|
162 |
}
|
163 |
}
|
169 |
$ip = $_SERVER['REMOTE_ADDR'];
|
170 |
|
171 |
$username = sanitize_user($username);
|
172 |
+
$user = get_user_by('login',$username);
|
173 |
if ( $user || "yes" == $loginlockdownOptions['lockout_invalid_usernames'] ) {
|
174 |
+
if ( $user === false ) {
|
175 |
+
$user_id = -1;
|
176 |
+
} else {
|
177 |
+
$user_id = $user->ID;
|
178 |
+
}
|
179 |
$insert = "INSERT INTO " . $table_name . " (user_id, lockdown_date, release_date, lockdown_IP) " .
|
180 |
+
"VALUES ('" . $user_id . "', now(), date_add(now(), INTERVAL " .
|
181 |
+
$loginlockdownOptions['lockout_length'] . " MINUTE), '%s')";
|
182 |
+
$insert = $wpdb->prepare( $insert, $ip );
|
183 |
$results = $wpdb->query($insert);
|
184 |
}
|
185 |
}
|
190 |
$ip = $_SERVER['REMOTE_ADDR'];
|
191 |
$class_c = substr ($ip, 0 , strrpos ( $ip, "." ));
|
192 |
|
193 |
+
$stillLockedquery = "SELECT user_id FROM $table_name " .
|
194 |
"WHERE release_date > now() AND " .
|
195 |
+
"lockdown_IP LIKE %s";
|
196 |
+
$stillLockedquery = $wpdb->prepare($stillLockedquery,$class_c . "%");
|
197 |
+
|
198 |
+
$stillLocked = $wpdb->get_var($stillLockedquery);
|
199 |
|
200 |
return $stillLocked;
|
201 |
}
|
214 |
$loginlockdownAdminOptions = array(
|
215 |
'max_login_retries' => 3,
|
216 |
'retries_within' => 5,
|
217 |
+
'lockout_length' => 60,
|
218 |
+
'lockout_invalid_usernames' => 'no',
|
219 |
+
'mask_login_errors' => 'no',
|
220 |
+
'show_credit_link' => 'yes'
|
221 |
+
);
|
222 |
$loginlockdownOptions = get_option("loginlockdownAdminOptions");
|
223 |
if ( !empty($loginlockdownOptions) ) {
|
224 |
foreach ( $loginlockdownOptions as $key => $option ) {
|
232 |
function print_loginlockdownAdminPage() {
|
233 |
global $wpdb;
|
234 |
$table_name = $wpdb->prefix . "lockdowns";
|
235 |
+
$loginlockdownAdminOptions = get_loginlockdownOptions();
|
236 |
+
|
237 |
if (isset($_POST['update_loginlockdownSettings'])) {
|
238 |
+
|
239 |
+
//wp_nonce check
|
240 |
+
check_admin_referer('login-lockdown_update-options');
|
241 |
+
|
242 |
if (isset($_POST['ll_max_login_retries'])) {
|
243 |
$loginlockdownAdminOptions['max_login_retries'] = $_POST['ll_max_login_retries'];
|
244 |
}
|
254 |
if (isset($_POST['ll_mask_login_errors'])) {
|
255 |
$loginlockdownAdminOptions['mask_login_errors'] = $_POST['ll_mask_login_errors'];
|
256 |
}
|
257 |
+
if (isset($_POST['ll_show_credit_link'])) {
|
258 |
+
$loginlockdownAdminOptions['show_credit_link'] = $_POST['ll_show_credit_link'];
|
259 |
+
}
|
260 |
update_option("loginlockdownAdminOptions", $loginlockdownAdminOptions);
|
261 |
?>
|
262 |
<div class="updated"><p><strong><?php _e("Settings Updated.", "loginlockdown");?></strong></p></div>
|
263 |
<?php
|
264 |
}
|
265 |
+
if (isset($_POST['release_lockdowns'])) {
|
266 |
+
|
267 |
+
//wp_nonce check
|
268 |
+
check_admin_referer('login-lockdown_release-lockdowns');
|
269 |
|
270 |
if (isset($_POST['releaseme'])) {
|
271 |
$released = $_POST['releaseme'];
|
272 |
foreach ( $released as $release_id ) {
|
273 |
+
$releasequery = "UPDATE $table_name SET release_date = now() " .
|
274 |
+
"WHERE lockdown_ID = '%d'";
|
275 |
+
$releasequery = $wpdb->prepare($releasequery,$release_id);
|
276 |
+
$results = $wpdb->query($releasequery);
|
277 |
}
|
278 |
}
|
279 |
update_option("loginlockdownAdminOptions", $loginlockdownAdminOptions);
|
283 |
}
|
284 |
$dalist = listLockedDown();
|
285 |
?>
|
286 |
+
<div class="wrap" style="width>
|
287 |
+
<form method="post" action="<?php echo esc_attr($_SERVER["REQUEST_URI"]); ?>">
|
288 |
+
<?php
|
289 |
+
if ( function_exists('wp_nonce_field') )
|
290 |
+
wp_nonce_field('login-lockdown_update-options');
|
291 |
?>
|
292 |
<h2><?php _e('Login LockDown Options', 'loginlockdown') ?></h2>
|
293 |
<h3><?php _e('Max Login Retries', 'loginlockdown') ?></h3>
|
294 |
+
<p>Number of failed login attempts within the "Retry Time Period Restriction" (defined below) needed to trigger a LockDown.</p>
|
295 |
+
<p><input type="text" name="ll_max_login_retries" size="8" value="<?php echo esc_attr($loginlockdownAdminOptions['max_login_retries']); ?>"></p>
|
296 |
<h3><?php _e('Retry Time Period Restriction (minutes)', 'loginlockdown') ?></h3>
|
297 |
+
<p>Amount of time that determines the rate at which failed login attempts are allowed before a LockDown occurs.</p>
|
298 |
+
<p><input type="text" name="ll_retries_within" size="8" value="<?php echo esc_attr($loginlockdownAdminOptions['retries_within']); ?>"></p>
|
299 |
<h3><?php _e('Lockout Length (minutes)', 'loginlockdown') ?></h3>
|
300 |
+
<p>How long a particular IP block will be locked out for once a LockDown has been triggered.</p>
|
301 |
+
<p><input type="text" name="ll_lockout_length" size="8" value="<?php echo esc_attr($loginlockdownAdminOptions['lockout_length']); ?>"></p>
|
302 |
<h3><?php _e('Lockout Invalid Usernames?', 'loginlockdown') ?></h3>
|
303 |
+
<p>By default Login LockDown will not trigger if an attempt is made to log in using a username that does not exist. You can override this behavior here.</p>
|
304 |
+
<p><input type="radio" name="ll_lockout_invalid_usernames" value="yes" <?php if( $loginlockdownAdminOptions['lockout_invalid_usernames'] == "yes" ) echo "checked"; ?>> Yes <input type="radio" name="ll_lockout_invalid_usernames" value="no" <?php if( $loginlockdownAdminOptions['lockout_invalid_usernames'] == "no" ) echo "checked"; ?>> No</p>
|
305 |
<h3><?php _e('Mask Login Errors?', 'loginlockdown') ?></h3>
|
306 |
+
<p>WordPress will normally display distinct messages to the user depending on whether they try and log in with an invalid username, or with a
|
307 |
+
valid username but the incorrect password. Toggling this option will hide why the login failed.</p>
|
308 |
+
<p><input type="radio" name="ll_mask_login_errors" value="yes" <?php if( $loginlockdownAdminOptions['mask_login_errors'] == "yes" ) echo "checked"; ?>> Yes <input type="radio" name="ll_mask_login_errors" value="no" <?php if( $loginlockdownAdminOptions['mask_login_errors'] == "no" ) echo "checked"; ?>> No</p>
|
309 |
+
<h3><?php _e('Show Credit Link?', 'loginlockdown') ?></h3>
|
310 |
+
<p>By default, Login LockDown will display the following message on the login form:<br />
|
311 |
+
<blockquote>Login form protected by <a href='http://www.bad-neighborhood.com/login-lockdown.html'>Login LockDown</a>.</blockquote>
|
312 |
+
This helps others know about the plugin so they can protect their blogs as well if they like. However, you can disable this message if you prefer.</p>
|
313 |
+
<input type="radio" name="ll_show_credit_link" value="yes" <?php if( $loginlockdownAdminOptions['show_credit_link'] == "yes" || $loginlockdownAdminOptions['show_credit_link'] == "" ) echo "checked"; ?>> Yes, display the credit link.<br />
|
314 |
+
<input type="radio" name="ll_show_credit_link" value="shownofollow" <?php if( $loginlockdownAdminOptions['show_credit_link'] == "shownofollow" ) echo "checked"; ?>> Display the credit link, but add "rel='nofollow'" (ie. do not pass any link juice).<br />
|
315 |
+
<input type="radio" name="ll_show_credit_link" value="no" <?php if( $loginlockdownAdminOptions['show_credit_link'] == "no" ) echo "checked"; ?>> No, do not display the credit link.<br />
|
316 |
<div class="submit">
|
317 |
+
<input type="submit" class="button button-primary" name="update_loginlockdownSettings" value="<?php _e('Update Settings', 'loginlockdown') ?>" /></div>
|
318 |
</form>
|
319 |
<br />
|
320 |
+
<form method="post" action="<?php echo esc_attr($_SERVER["REQUEST_URI"]); ?>">
|
321 |
+
<?php
|
322 |
+
if ( function_exists('wp_nonce_field') )
|
323 |
+
wp_nonce_field('login-lockdown_release-lockdowns');
|
324 |
?>
|
325 |
<h3><?php _e('Currently Locked Out', 'loginlockdown') ?></h3>
|
326 |
<?php
|
327 |
$num_lockedout = count($dalist);
|
328 |
if( 0 == $num_lockedout ) {
|
329 |
+
echo "<p>No IP blocks currently locked out.</p>";
|
330 |
} else {
|
331 |
foreach ( $dalist as $key => $option ) {
|
332 |
?>
|
336 |
}
|
337 |
?>
|
338 |
<div class="submit">
|
339 |
+
<input type="submit" class="button button-primary" name="release_lockdowns" value="<?php _e('Release Selected', 'loginlockdown') ?>" /></div>
|
340 |
</form>
|
341 |
</div>
|
342 |
<?php
|
344 |
|
345 |
function loginlockdown_ap() {
|
346 |
if ( function_exists('add_options_page') ) {
|
347 |
+
add_options_page('Login LockDown', 'Login LockDown', 'manage_options', basename(__FILE__), 'print_loginlockdownAdminPage');
|
348 |
}
|
349 |
}
|
350 |
|
351 |
function ll_credit_link(){
|
352 |
+
global $loginlockdownOptions;
|
353 |
+
$thispage = "http://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"];
|
354 |
+
$homepage = get_option( "home" );
|
355 |
+
$showcreditlink = $loginlockdownOptions['show_credit_link'];
|
356 |
+
$relnofollow = "rel='nofollow'";
|
357 |
+
if ( $showcreditlink != "shownofollow" && ($thispage == $homepage || $thispage == $homepage . "/" || substr($_SERVER["REQUEST_URI"], strlen($_SERVER["REQUEST_URI"]) - 12) == "wp-login.php") ) {
|
358 |
+
$relnofollow = "";
|
359 |
+
}
|
360 |
+
if ( $showcreditlink != "no" ) {
|
361 |
+
echo "<p>Login form protected by <a href='http://www.bad-neighborhood.com/login-lockdown.html' $relnofollow>Login LockDown</a>.<br /><br /><br /></p>";
|
362 |
+
}
|
363 |
}
|
364 |
|
365 |
//Actions and Filters
|
366 |
if ( isset($loginlockdown_db_version) ) {
|
367 |
//Actions
|
368 |
add_action('admin_menu', 'loginlockdown_ap');
|
369 |
+
if(!defined('WP_PLUGIN_DIR')){
|
370 |
define('WP_PLUGIN_DIR', ABSPATH . 'wp-content/plugins');
|
371 |
}
|
372 |
+
$activatestr = str_replace(WP_PLUGIN_DIR . "/", "activate_", __FILE__);
|
373 |
add_action($activatestr, 'loginLockdown_install');
|
374 |
+
add_action('login_form', 'll_credit_link');
|
375 |
+
|
376 |
+
remove_filter('authenticate', 'wp_authenticate_username_password', 20, 3);
|
377 |
add_filter('authenticate', 'll_wp_authenticate_username_password', 20, 3);
|
378 |
//Filters
|
379 |
+
//Functions
|
380 |
+
function ll_wp_authenticate_username_password($user, $username, $password) {
|
381 |
+
if ( is_a($user, 'WP_User') ) { return $user; }
|
382 |
+
|
383 |
+
if ( empty($username) || empty($password) ) {
|
384 |
+
$error = new WP_Error();
|
385 |
+
|
386 |
+
if ( empty($username) )
|
387 |
+
$error->add('empty_username', __('<strong>ERROR</strong>: The username field is empty.'));
|
388 |
+
|
389 |
+
if ( empty($password) )
|
390 |
+
$error->add('empty_password', __('<strong>ERROR</strong>: The password field is empty.'));
|
391 |
+
|
392 |
+
return $error;
|
393 |
+
}
|
394 |
+
|
395 |
+
$userdata = get_user_by('login',$username);
|
396 |
+
|
397 |
+
if ( !$userdata ) {
|
398 |
+
return new WP_Error('invalid_username', sprintf(__('<strong>ERROR</strong>: Invalid username. <a href="%s" title="Password Lost and Found">Lost your password</a>?'), site_url('wp-login.php?action=lostpassword', 'login')));
|
399 |
+
}
|
400 |
+
|
401 |
+
$userdata = apply_filters('wp_authenticate_user', $userdata, $password);
|
402 |
+
if ( is_wp_error($userdata) ) {
|
403 |
+
return $userdata;
|
404 |
+
}
|
405 |
+
|
406 |
+
if ( !wp_check_password($password, $userdata->user_pass, $userdata->ID) ) {
|
407 |
+
return new WP_Error('incorrect_password', sprintf(__('<strong>ERROR</strong>: Incorrect password. <a href="%s" title="Password Lost and Found">Lost your password</a>?'), site_url('wp-login.php?action=lostpassword', 'login')));
|
408 |
+
}
|
409 |
+
|
410 |
+
$user = new WP_User($userdata->ID);
|
411 |
+
return $user;
|
412 |
+
}
|
413 |
+
|
414 |
|
415 |
if ( !function_exists('wp_authenticate') ) :
|
416 |
function wp_authenticate($username, $password) {
|
417 |
global $wpdb, $error;
|
418 |
+
global $loginlockdownOptions;
|
419 |
+
|
420 |
+
$username = sanitize_user($username);
|
421 |
+
$password = trim($password);
|
422 |
+
|
423 |
if ( "" != isLockedDown() ) {
|
424 |
return new WP_Error('incorrect_password', "<strong>ERROR</strong>: We're sorry, but this IP range has been blocked due to too many recent " .
|
425 |
"failed login attempts.<br /><br />Please try again later.");
|
426 |
}
|
427 |
+
|
428 |
+
$user = apply_filters('authenticate', null, $username, $password);
|
429 |
+
|
430 |
+
if ( $user == null ) {
|
431 |
+
// TODO what should the error message be? (Or would these even happen?)
|
432 |
+
// Only needed if all authentication handlers fail to return anything.
|
433 |
+
$user = new WP_Error('authentication_failed', __('<strong>ERROR</strong>: Invalid username or incorrect password.'));
|
434 |
+
}
|
435 |
+
|
436 |
+
$ignore_codes = array('empty_username', 'empty_password');
|
437 |
+
|
438 |
+
if (is_wp_error($user) && !in_array($user->get_error_code(), $ignore_codes) ) {
|
439 |
incrementFails($username);
|
440 |
if ( $loginlockdownOptions['max_login_retries'] <= countFails($username) ) {
|
441 |
lockDown($username);
|
442 |
return new WP_Error('incorrect_password', __("<strong>ERROR</strong>: We're sorry, but this IP range has been blocked due to too many recent " .
|
443 |
"failed login attempts.<br /><br />Please try again later."));
|
444 |
+
}
|
445 |
+
if ( 'yes' == $loginlockdownOptions['mask_login_errors'] ) {
|
446 |
+
return new WP_Error('authentication_failed', sprintf(__('<strong>ERROR</strong>: Invalid username or incorrect password. <a href="%s" title="Password Lost and Found">Lost your password</a>?'), site_url('wp-login.php?action=lostpassword', 'login')));
|
447 |
} else {
|
448 |
+
do_action('wp_login_failed', $username);
|
449 |
+
}
|
450 |
+
}
|
451 |
+
|
452 |
return $user;
|
453 |
}
|
454 |
endif;
|
455 |
}
|
456 |
|
457 |
|
458 |
+
?>
|
readme.txt
CHANGED
@@ -1,9 +1,9 @@
|
|
1 |
=== Login LockDown ===
|
2 |
Developer: Michael VanDeMar (michael@endlesspoetry.com)
|
3 |
Tags: security, login
|
4 |
-
Requires at least:
|
5 |
-
Tested up to:
|
6 |
-
Stable Tag: 1.
|
7 |
|
8 |
Limits the number of login attempts from a given IP range within a certain time period.
|
9 |
|
@@ -22,4 +22,45 @@ via the Options panel. Admisitrators can release locked out IP ranges manually f
|
|
22 |
2. Activate the plugin in the Plugin options.
|
23 |
3. Customize the settings from the Options panel, if desired.
|
24 |
|
25 |
-
Enjoy.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
=== Login LockDown ===
|
2 |
Developer: Michael VanDeMar (michael@endlesspoetry.com)
|
3 |
Tags: security, login
|
4 |
+
Requires at least: 3.6
|
5 |
+
Tested up to: 3.8.1
|
6 |
+
Stable Tag: 1.6
|
7 |
|
8 |
Limits the number of login attempts from a given IP range within a certain time period.
|
9 |
|
22 |
2. Activate the plugin in the Plugin options.
|
23 |
3. Customize the settings from the Options panel, if desired.
|
24 |
|
25 |
+
Enjoy.
|
26 |
+
|
27 |
+
== Change Log ==
|
28 |
+
|
29 |
+
ver. 1.6 7-Mar-2014
|
30 |
+
|
31 |
+
- cleaned up deprecated functions
|
32 |
+
- fixed bug with invalid property on a non-object when locking out invalid usernames
|
33 |
+
- fixed utilization of $wpdb->prepare
|
34 |
+
- added more descriptive help text to each of the options
|
35 |
+
- added the ability to remove the "Login form protected by Login LockDown." message from within the dashboard
|
36 |
+
|
37 |
+
ver. 1.5 17-Sep-2009
|
38 |
+
|
39 |
+
- implemented wp_nonce security in the options and lockdown release forms in the admin screen
|
40 |
+
- fixed a security hole with an improperly escaped SQL query
|
41 |
+
- encoded certain outputs in the admin panel using esc_attr() to prevent XSS attacks
|
42 |
+
- fixed an issue with the 'Lockout Invalid Usernames' option not functioning as intended
|
43 |
+
|
44 |
+
ver. 1.4 29-Aug-2009
|
45 |
+
|
46 |
+
- removed erroneous error affecting WP 2.8+
|
47 |
+
- fixed activation error caused by customizing the location of the wp-content folder
|
48 |
+
- added in the option to mask which specific login error (invalid username or invalid password) was generated
|
49 |
+
- added in the option to lock out failed login attempts even if the username doesn't exist
|
50 |
+
|
51 |
+
ver. 1.3 23-Feb-2009
|
52 |
+
- adjusted positioning of plugin byline
|
53 |
+
- allowed for dynamic location of plugin files
|
54 |
+
|
55 |
+
ver. 1.2 15-Jun-2008
|
56 |
+
|
57 |
+
- now compatible with WordPress 2.5 and up only
|
58 |
+
|
59 |
+
ver. 1.1 01-Sep-2007
|
60 |
+
|
61 |
+
- revised time query to MySQL 4.0 compatability
|
62 |
+
|
63 |
+
ver. 1.0 29-Aug-2007
|
64 |
+
|
65 |
+
- released
|
66 |
+
|
version.txt
CHANGED
@@ -1 +1 @@
|
|
1 |
-
1.
|
1 |
+
1.6
|