Login LockDown - Version 1.7.1

Version Description

Download this release

Release Info

Developer mvandemar
Plugin Icon 128x128 Login LockDown
Version 1.7.1
Comparing to
See all releases

Code changes from version 1.6.1 to 1.7.1

Files changed (3) hide show
  1. loginlockdown.php +124 -16
  2. readme.txt +11 -4
  3. version.txt +1 -1
loginlockdown.php CHANGED
@@ -2,7 +2,7 @@
2
  /*
3
  Plugin Name: Login LockDown
4
  Plugin URI: http://www.bad-neighborhood.com/
5
- Version: v1.6.1
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
  */
@@ -10,6 +10,12 @@ Description: Adds some extra security to WordPress by restricting the rate at wh
10
  /*
11
  == Change Log ==
12
  *
 
 
 
 
 
 
13
  * ver. 1.6.1 8-Mar-2014
14
  * - fixed html glitch preventing options from being saved
15
  *
@@ -131,14 +137,13 @@ function countFails($username = "") {
131
  global $wpdb;
132
  global $loginlockdownOptions;
133
  $table_name = $wpdb->prefix . "login_fails";
134
- $ip = $_SERVER['REMOTE_ADDR'];
135
- $class_c = substr ($ip, 0 , strrpos ( $ip, "." ));
136
 
137
  $numFailsquery = "SELECT COUNT(login_attempt_ID) FROM $table_name " .
138
  "WHERE login_attempt_date + INTERVAL " .
139
  $loginlockdownOptions['retries_within'] . " MINUTE > now() AND " .
140
  "login_attempt_IP LIKE '%s'";
141
- $numFailsquery = $wpdb->prepare( $numFailsquery, $class_c . "%");
142
 
143
  $numFails = $wpdb->get_var($numFailsquery);
144
  return $numFails;
@@ -148,7 +153,7 @@ function incrementFails($username = "") {
148
  global $wpdb;
149
  global $loginlockdownOptions;
150
  $table_name = $wpdb->prefix . "login_fails";
151
- $ip = $_SERVER['REMOTE_ADDR'];
152
 
153
  $username = sanitize_user($username);
154
  $user = get_user_by('login',$username);
@@ -160,7 +165,7 @@ function incrementFails($username = "") {
160
  }
161
  $insert = "INSERT INTO " . $table_name . " (user_id, login_attempt_date, login_attempt_IP) " .
162
  "VALUES ('" . $user_id . "', now(), '%s')";
163
- $insert = $wpdb->prepare( $insert, $ip );
164
  $results = $wpdb->query($insert);
165
  }
166
  }
@@ -169,7 +174,7 @@ function lockDown($username = "") {
169
  global $wpdb;
170
  global $loginlockdownOptions;
171
  $table_name = $wpdb->prefix . "lockdowns";
172
- $ip = $_SERVER['REMOTE_ADDR'];
173
 
174
  $username = sanitize_user($username);
175
  $user = get_user_by('login',$username);
@@ -182,7 +187,7 @@ function lockDown($username = "") {
182
  $insert = "INSERT INTO " . $table_name . " (user_id, lockdown_date, release_date, lockdown_IP) " .
183
  "VALUES ('" . $user_id . "', now(), date_add(now(), INTERVAL " .
184
  $loginlockdownOptions['lockout_length'] . " MINUTE), '%s')";
185
- $insert = $wpdb->prepare( $insert, $ip );
186
  $results = $wpdb->query($insert);
187
  }
188
  }
@@ -190,13 +195,12 @@ function lockDown($username = "") {
190
  function isLockedDown() {
191
  global $wpdb;
192
  $table_name = $wpdb->prefix . "lockdowns";
193
- $ip = $_SERVER['REMOTE_ADDR'];
194
- $class_c = substr ($ip, 0 , strrpos ( $ip, "." ));
195
 
196
  $stillLockedquery = "SELECT user_id FROM $table_name " .
197
  "WHERE release_date > now() AND " .
198
  "lockdown_IP LIKE %s";
199
- $stillLockedquery = $wpdb->prepare($stillLockedquery,$class_c . "%");
200
 
201
  $stillLocked = $wpdb->get_var($stillLockedquery);
202
 
@@ -232,6 +236,27 @@ function get_loginlockdownOptions() {
232
  return $loginlockdownAdminOptions;
233
  }
234
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
  function print_loginlockdownAdminPage() {
236
  global $wpdb;
237
  $table_name = $wpdb->prefix . "lockdowns";
@@ -287,12 +312,24 @@ function print_loginlockdownAdminPage() {
287
  $dalist = listLockedDown();
288
  ?>
289
  <div class="wrap">
 
 
 
 
 
 
 
 
 
 
 
 
290
  <form method="post" action="<?php echo esc_attr($_SERVER["REQUEST_URI"]); ?>">
291
  <?php
292
  if ( function_exists('wp_nonce_field') )
293
  wp_nonce_field('login-lockdown_update-options');
294
  ?>
295
- <h2><?php _e('Login LockDown Options', 'loginlockdown') ?></h2>
296
  <h3><?php _e('Max Login Retries', 'loginlockdown') ?></h3>
297
  <p>Number of failed login attempts within the "Retry Time Period Restriction" (defined below) needed to trigger a LockDown.</p>
298
  <p><input type="text" name="ll_max_login_retries" size="8" value="<?php echo esc_attr($loginlockdownAdminOptions['max_login_retries']); ?>"></p>
@@ -319,13 +356,20 @@ This helps others know about the plugin so they can protect their blogs as well
319
  <div class="submit">
320
  <input type="submit" class="button button-primary" name="update_loginlockdownSettings" value="<?php _e('Update Settings', 'loginlockdown') ?>" /></div>
321
  </form>
322
- <br />
323
  <form method="post" action="<?php echo esc_attr($_SERVER["REQUEST_URI"]); ?>">
324
  <?php
325
  if ( function_exists('wp_nonce_field') )
326
  wp_nonce_field('login-lockdown_release-lockdowns');
327
  ?>
328
- <h3><?php _e('Currently Locked Out', 'loginlockdown') ?></h3>
 
 
 
 
 
 
 
329
  <?php
330
  $num_lockedout = count($dalist);
331
  if( 0 == $num_lockedout ) {
@@ -341,6 +385,7 @@ if ( function_exists('wp_nonce_field') )
341
  <div class="submit">
342
  <input type="submit" class="button button-primary" name="release_lockdowns" value="<?php _e('Release Selected', 'loginlockdown') ?>" /></div>
343
  </form>
 
344
  </div>
345
  <?php
346
  }//End function print_loginlockdownAdminPage()
@@ -455,7 +500,70 @@ if ( isset($loginlockdown_db_version) ) {
455
  return $user;
456
  }
457
  endif;
458
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
459
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
460
 
461
- ?>
2
  /*
3
  Plugin Name: Login LockDown
4
  Plugin URI: http://www.bad-neighborhood.com/
5
+ Version: v1.7.1
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
  */
10
  /*
11
  == Change Log ==
12
  *
13
+ * ver. 1.7.1 13-Sep-2016
14
+ * - fixed bug causing all ipv6 addresses to get locked out if 1 was
15
+ * - added in WordPress MultiSite functionality
16
+ * - fixed bug where subnets could be overly matched, causing more IPs to be blocked than intended
17
+ * - moved the report for locked out IP addresses to its own tab
18
+ *
19
  * ver. 1.6.1 8-Mar-2014
20
  * - fixed html glitch preventing options from being saved
21
  *
137
  global $wpdb;
138
  global $loginlockdownOptions;
139
  $table_name = $wpdb->prefix . "login_fails";
140
+ $subnet = calc_subnet($_SERVER['REMOTE_ADDR']);
 
141
 
142
  $numFailsquery = "SELECT COUNT(login_attempt_ID) FROM $table_name " .
143
  "WHERE login_attempt_date + INTERVAL " .
144
  $loginlockdownOptions['retries_within'] . " MINUTE > now() AND " .
145
  "login_attempt_IP LIKE '%s'";
146
+ $numFailsquery = $wpdb->prepare( $numFailsquery, $subnet[1] . "%");
147
 
148
  $numFails = $wpdb->get_var($numFailsquery);
149
  return $numFails;
153
  global $wpdb;
154
  global $loginlockdownOptions;
155
  $table_name = $wpdb->prefix . "login_fails";
156
+ $subnet = calc_subnet($_SERVER['REMOTE_ADDR']);
157
 
158
  $username = sanitize_user($username);
159
  $user = get_user_by('login',$username);
165
  }
166
  $insert = "INSERT INTO " . $table_name . " (user_id, login_attempt_date, login_attempt_IP) " .
167
  "VALUES ('" . $user_id . "', now(), '%s')";
168
+ $insert = $wpdb->prepare( $insert, $subnet[0] );
169
  $results = $wpdb->query($insert);
170
  }
171
  }
174
  global $wpdb;
175
  global $loginlockdownOptions;
176
  $table_name = $wpdb->prefix . "lockdowns";
177
+ $subnet = calc_subnet($_SERVER['REMOTE_ADDR']);
178
 
179
  $username = sanitize_user($username);
180
  $user = get_user_by('login',$username);
187
  $insert = "INSERT INTO " . $table_name . " (user_id, lockdown_date, release_date, lockdown_IP) " .
188
  "VALUES ('" . $user_id . "', now(), date_add(now(), INTERVAL " .
189
  $loginlockdownOptions['lockout_length'] . " MINUTE), '%s')";
190
+ $insert = $wpdb->prepare( $insert, $subnet[0] );
191
  $results = $wpdb->query($insert);
192
  }
193
  }
195
  function isLockedDown() {
196
  global $wpdb;
197
  $table_name = $wpdb->prefix . "lockdowns";
198
+ $subnet = calc_subnet($_SERVER['REMOTE_ADDR']);
 
199
 
200
  $stillLockedquery = "SELECT user_id FROM $table_name " .
201
  "WHERE release_date > now() AND " .
202
  "lockdown_IP LIKE %s";
203
+ $stillLockedquery = $wpdb->prepare($stillLockedquery,$subnet[1] . "%");
204
 
205
  $stillLocked = $wpdb->get_var($stillLockedquery);
206
 
236
  return $loginlockdownAdminOptions;
237
  }
238
 
239
+ function calc_subnet($ip) {
240
+ $subnet[0] = $ip;
241
+ if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
242
+ $ip = expandipv6($ip);
243
+ preg_match("/^([0-9abcdef]{1,4}:){4}/", $ip, $matches);
244
+ $subnet[0] = $ip;
245
+ $subnet[1] = $matches[0];
246
+ } else {
247
+ $subnet[1] = substr ($ip, 0 , strrpos ( $ip, "." ) + 1);
248
+ }
249
+ return $subnet;
250
+ }
251
+
252
+ function expandipv6($ip){
253
+ $hex = unpack("H*hex", inet_pton($ip));
254
+ $ip = substr(preg_replace("/([A-f0-9]{4})/", "$1:", $hex['hex']), 0, -1);
255
+
256
+ return $ip;
257
+ }
258
+
259
+
260
  function print_loginlockdownAdminPage() {
261
  global $wpdb;
262
  $table_name = $wpdb->prefix . "lockdowns";
312
  $dalist = listLockedDown();
313
  ?>
314
  <div class="wrap">
315
+ <?php
316
+
317
+ $active_tab = isset( $_GET[ 'tab' ] ) ? $_GET[ 'tab' ] : 'settings';
318
+
319
+ ?>
320
+ <h2><?php _e('Login LockDown Options', 'loginlockdown') ?></h2>
321
+
322
+ <h2 class="nav-tab-wrapper">
323
+ <a href="?page=loginlockdown.php&tab=settings" class="nav-tab <?php echo $active_tab == 'settings' ? 'nav-tab-active' : ''; ?>">Settings</a>
324
+ <a href="?page=loginlockdown.php&tab=activity" class="nav-tab <?php echo $active_tab == 'activity' ? 'nav-tab-active' : ''; ?>">Activity (<?php echo count($dalist); ?>)</a>
325
+ </h2>
326
+ <?php if ( $active_tab == 'settings' ) { ?>
327
  <form method="post" action="<?php echo esc_attr($_SERVER["REQUEST_URI"]); ?>">
328
  <?php
329
  if ( function_exists('wp_nonce_field') )
330
  wp_nonce_field('login-lockdown_update-options');
331
  ?>
332
+
333
  <h3><?php _e('Max Login Retries', 'loginlockdown') ?></h3>
334
  <p>Number of failed login attempts within the "Retry Time Period Restriction" (defined below) needed to trigger a LockDown.</p>
335
  <p><input type="text" name="ll_max_login_retries" size="8" value="<?php echo esc_attr($loginlockdownAdminOptions['max_login_retries']); ?>"></p>
356
  <div class="submit">
357
  <input type="submit" class="button button-primary" name="update_loginlockdownSettings" value="<?php _e('Update Settings', 'loginlockdown') ?>" /></div>
358
  </form>
359
+ <?php } else { ?>
360
  <form method="post" action="<?php echo esc_attr($_SERVER["REQUEST_URI"]); ?>">
361
  <?php
362
  if ( function_exists('wp_nonce_field') )
363
  wp_nonce_field('login-lockdown_release-lockdowns');
364
  ?>
365
+ <h3><?php
366
+ if( count($dalist) == 1 ) {
367
+ printf( esc_html__( 'There is currently %d locked out IP address.', 'loginlockdown' ), count($dalist) );
368
+
369
+ } else {
370
+ printf( esc_html__( 'There are currently %d locked out IP addresses.', 'loginlockdown' ), count($dalist) );
371
+ } ?></h3>
372
+
373
  <?php
374
  $num_lockedout = count($dalist);
375
  if( 0 == $num_lockedout ) {
385
  <div class="submit">
386
  <input type="submit" class="button button-primary" name="release_lockdowns" value="<?php _e('Release Selected', 'loginlockdown') ?>" /></div>
387
  </form>
388
+ <?php } ?>
389
  </div>
390
  <?php
391
  }//End function print_loginlockdownAdminPage()
500
  return $user;
501
  }
502
  endif;
503
+ // multisite network-wide activation
504
+ register_activation_hook( __FILE__, 'loginlockdown_multisite_activate' );
505
+ function loginlockdown_multisite_activate($networkwide) {
506
+ global $wpdb;
507
+
508
+ if (function_exists('is_multisite') && is_multisite()) {
509
+ // check if it is a network activation - if so, run the activation function for each blog id
510
+ if ($networkwide) {
511
+ $old_blog = $wpdb->blogid;
512
+ // Get all blog ids
513
+ $blogids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs");
514
+ foreach ($blogids as $blog_id) {
515
+ switch_to_blog($blog_id);
516
+ loginLockdown_install();
517
+ }
518
+ switch_to_blog($old_blog);
519
+ return;
520
+ }
521
+ }
522
+ }
523
 
524
+ // multisite new site activation
525
+ add_action( 'wpmu_new_blog', 'loginlockdown_multisite_newsite', 10, 6);
526
+ function loginlockdown_multisite_newsite($blog_id, $user_id, $domain, $path, $site_id, $meta ) {
527
+ global $wpdb;
528
+
529
+ if (is_plugin_active_for_network('loginlockdown/loginlockdown.php')) {
530
+ $old_blog = $wpdb->blogid;
531
+ switch_to_blog($blog_id);
532
+ loginLockdown_install();
533
+ switch_to_blog($old_blog);
534
+ }
535
+ }
536
+
537
+ // multisite old sites check
538
+
539
+ add_action('admin_init','loginlockdown_multisite_legacy');
540
+ function loginlockdown_multisite_legacy() {
541
+ $loginlockdownMSRunOnce = get_option("loginlockdownmsrunonce");
542
+ if ( empty($loginlockdownMSRunOnce) ) {
543
+ global $wpdb;
544
+
545
+ if (function_exists('is_multisite') && is_multisite()) {
546
+
547
+ $old_blog = $wpdb->blogid;
548
+
549
+ // Get all blog ids
550
+ $blogids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs");
551
+ foreach ($blogids as $blog_id) {
552
+
553
+ // check if already exists
554
+ $bed_check = $wpdb->query("SHOW TABLES LIKE '{$wpdb->base_prefix}{$blog_id}_login_fails'");
555
+ if (!$bed_check) {
556
+
557
+ switch_to_blog($blog_id);
558
+ loginLockdown_install();
559
+
560
+ }
561
+ }
562
+ switch_to_blog($old_blog);
563
+ }
564
+ add_option("loginlockdownmsrunonce", "done", "", "no");
565
+ return;
566
+ }
567
+ }
568
+ }
569
 
 
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: 3.6
5
- Tested up to: 4.0
6
- Stable Tag: 1.6.1
7
 
8
  Limits the number of login attempts from a given IP range within a certain time period.
9
 
@@ -14,7 +14,7 @@ certain number of attempts are detected within a short period of time from the s
14
  IP range, then the login function is disabled for all requests from that range.
15
  This helps to prevent brute force password discovery. Currently the plugin defaults
16
  to a 1 hour lock out of an IP block after 3 failed login attempts within 5 minutes. This can be modified
17
- via the Options panel. Admisitrators can release locked out IP ranges manually from the panel.
18
 
19
  == Installation ==
20
 
@@ -26,6 +26,13 @@ Enjoy.
26
 
27
  == Change Log ==
28
 
 
 
 
 
 
 
 
29
  ver. 1.6.1 8-Mar-2014
30
 
31
  - fixed html glitch preventing options from being saved
1
  === Login LockDown ===
2
  Developer: Michael VanDeMar (michael@endlesspoetry.com)
3
+ Tags: security, login, login form
4
  Requires at least: 3.6
5
+ Tested up to: 4.9.6
6
+ Stable Tag: 1.7.1
7
 
8
  Limits the number of login attempts from a given IP range within a certain time period.
9
 
14
  IP range, then the login function is disabled for all requests from that range.
15
  This helps to prevent brute force password discovery. Currently the plugin defaults
16
  to a 1 hour lock out of an IP block after 3 failed login attempts within 5 minutes. This can be modified
17
+ via the Options panel. Administrators can release locked out IP ranges manually from the panel.
18
 
19
  == Installation ==
20
 
26
 
27
  == Change Log ==
28
 
29
+ ver. 1.7.1 13-Sep-2016
30
+
31
+ - fixed bug causing all ipv6 addresses to get locked out if 1 was
32
+ - added in WordPress MultiSite functionality
33
+ - fixed bug where subnets could be overly matched, causing more IPs to be blocked than intended
34
+ - moved the report for locked out IP addresses to its own tab
35
+
36
  ver. 1.6.1 8-Mar-2014
37
 
38
  - fixed html glitch preventing options from being saved
version.txt CHANGED
@@ -1 +1 @@
1
- 1.6.1
1
+ 1.7.1