Limit Login Attempts - Version 2.0beta1

Version Description

Download this release

Release Info

Developer johanee
Plugin Icon wp plugin Limit Login Attempts
Version 2.0beta1
Comparing to
See all releases

Code changes from version 1.3.1 to 2.0beta1

Files changed (3) hide show
  1. images/icon_bad.gif +0 -0
  2. limit-login-attempts.php +530 -102
  3. readme.txt +33 -2
images/icon_bad.gif ADDED
Binary file
limit-login-attempts.php CHANGED
@@ -5,7 +5,7 @@
5
  Description: Limit rate of login attempts, including by way of cookies, for each IP.
6
  Author: Johan Eenfeldt
7
  Author URI: http://devel.kostdoktorn.se
8
- Version: 1.3.1
9
 
10
  Copyright 2008, 2009 Johan Eenfeldt
11
 
@@ -76,12 +76,38 @@ $limit_login_options =
76
 
77
  /* If notify by email, do so after this number of lockouts */
78
  , 'notify_email_after' => 4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  );
80
 
81
  $limit_login_my_error_shown = false; /* have we shown our stuff? */
82
  $limit_login_just_lockedout = false; /* started this pageload??? */
83
  $limit_login_nonempty_credentials = false; /* user and pwd nonempty */
84
 
 
 
 
 
 
85
 
86
  /*
87
  * Startup
@@ -113,17 +139,12 @@ function limit_login_setup() {
113
  add_action('login_head', 'limit_login_add_error_message');
114
  add_action('login_errors', 'limit_login_fixup_error_messages');
115
  add_action('admin_menu', 'limit_login_admin_menu');
116
- }
117
-
118
-
119
- /* Get current option value */
120
- function limit_login_option($option_name) {
121
- global $limit_login_options;
122
-
123
- if (isset($limit_login_options[$option_name])) {
124
- return $limit_login_options[$option_name];
125
- } else {
126
- return null;
127
  }
128
  }
129
 
@@ -172,6 +193,24 @@ function is_limit_login_ok() {
172
  }
173
 
174
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
  /* Filter: allow login attempt? (called from wp_authenticate()) */
176
  function limit_login_wp_authenticate_user($user, $password) {
177
  if (is_wp_error($user) || is_limit_login_ok() ) {
@@ -325,29 +364,209 @@ function limit_login_cleanup($retries = null, $lockouts = null, $valid = null) {
325
  /* remove retries that are no longer valid */
326
  $valid = !is_null($valid) ? $valid : get_option('limit_login_retries_valid');
327
  $retries = !is_null($retries) ? $retries : get_option('limit_login_retries');
328
- if (!is_array($valid) || !is_array($retries)) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
329
  return;
330
  }
331
 
332
- foreach ($valid as $ip => $lockout) {
333
- if ($lockout < $now) {
334
- unset($valid[$ip]);
335
- unset($retries[$ip]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
336
  }
337
  }
338
 
339
- /* go through retries directly, if for some reason they've gone out of sync */
340
- foreach ($retries as $ip => $retry) {
341
- if (!isset($valid[$ip])) {
342
- unset($retries[$ip]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
343
  }
344
  }
345
 
346
- update_option('limit_login_retries', $retries);
347
- update_option('limit_login_retries_valid', $valid);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
348
  }
349
 
350
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
351
  /* Email notification of lockout to admin (if configured) */
352
  function limit_login_notify_email($user) {
353
  $ip = limit_login_get_address();
@@ -439,16 +658,39 @@ function limit_login_notify($user) {
439
  }
440
 
441
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
442
  /* Construct informative error message */
443
- function limit_login_error_msg() {
444
  $ip = limit_login_get_address();
445
- $lockouts = get_option('limit_login_lockouts');
446
 
447
- $msg = __('<strong>ERROR</strong>: Too many failed login attempts.', 'limit-login-attempts') . ' ';
 
 
448
 
449
  if (!is_array($lockouts) || !isset($lockouts[$ip]) || time() >= $lockouts[$ip]) {
450
- /* Huh? No timeout active? */
451
- $msg .= __('Please try again later.', 'limit-login-attempts');
452
  return $msg;
453
  }
454
 
@@ -515,6 +757,19 @@ function should_limit_login_show_msg() {
515
  }
516
 
517
 
 
 
 
 
 
 
 
 
 
 
 
 
 
518
  /* Fix up the error message before showing it */
519
  function limit_login_fixup_error_messages($content) {
520
  global $limit_login_just_lockedout, $limit_login_nonempty_credentials, $limit_login_my_error_shown;
@@ -575,6 +830,12 @@ function limit_login_fixup_error_messages($content) {
575
  function limit_login_add_error_message() {
576
  global $error, $limit_login_my_error_shown;
577
 
 
 
 
 
 
 
578
  if (!should_limit_login_show_msg() || $limit_login_my_error_shown) {
579
  return;
580
  }
@@ -598,10 +859,6 @@ function limit_login_track_credentials($user, $password) {
598
  }
599
 
600
 
601
- /*
602
- * Admin stuff
603
- */
604
-
605
  /* Does wordpress version support cookie option? */
606
  function limit_login_support_cookie_option() {
607
  global $wp_version;
@@ -609,10 +866,19 @@ function limit_login_support_cookie_option() {
609
  }
610
 
611
 
612
- /* Make a guess if we are behind a proxy or not */
613
- function limit_login_guess_proxy() {
614
- return isset($_SERVER[LIMIT_LOGIN_PROXY_ADDR])
615
- ? LIMIT_LOGIN_PROXY_ADDR : LIMIT_LOGIN_DIRECT_ADDR;
 
 
 
 
 
 
 
 
 
616
  }
617
 
618
 
@@ -623,6 +889,14 @@ function limit_login_get_option($option, $var_name) {
623
  if ($a !== false) {
624
  global $limit_login_options;
625
 
 
 
 
 
 
 
 
 
626
  $limit_login_options[$var_name] = $a;
627
  }
628
  }
@@ -630,15 +904,11 @@ function limit_login_get_option($option, $var_name) {
630
 
631
  /* Setup global variables from options */
632
  function limit_login_setup_options() {
633
- limit_login_get_option('limit_login_client_type', 'client_type');
634
- limit_login_get_option('limit_login_allowed_retries', 'allowed_retries');
635
- limit_login_get_option('limit_login_lockout_duration', 'lockout_duration');
636
- limit_login_get_option('limit_login_valid_duration', 'valid_duration');
637
- limit_login_get_option('limit_login_cookies', 'cookies');
638
- limit_login_get_option('limit_login_lockout_notify', 'lockout_notify');
639
- limit_login_get_option('limit_login_allowed_lockouts', 'allowed_lockouts');
640
- limit_login_get_option('limit_login_long_duration', 'long_duration');
641
- limit_login_get_option('limit_login_notify_email_after', 'notify_email_after');
642
 
643
  limit_login_sanitize_variables();
644
  }
@@ -646,23 +916,14 @@ function limit_login_setup_options() {
646
 
647
  /* Update options in db from global variables */
648
  function limit_login_update_options() {
649
- update_option('limit_login_client_type', limit_login_option('client_type'));
650
- update_option('limit_login_allowed_retries', limit_login_option('allowed_retries'));
651
- update_option('limit_login_lockout_duration', limit_login_option('lockout_duration'));
652
- update_option('limit_login_allowed_lockouts', limit_login_option('allowed_lockouts'));
653
- update_option('limit_login_long_duration', limit_login_option('long_duration'));
654
- update_option('limit_login_valid_duration', limit_login_option('valid_duration'));
655
- update_option('limit_login_lockout_notify', limit_login_option('lockout_notify'));
656
- update_option('limit_login_notify_email_after', limit_login_option('notify_email_after'));
657
- update_option('limit_login_cookies', limit_login_option('cookies') ? '1' : '0');
658
- }
659
-
660
-
661
- /* Make sure the variables make sense -- simple integer */
662
- function limit_login_sanitize_simple_int($var_name) {
663
  global $limit_login_options;
664
 
665
- $limit_login_options[$var_name] = max(1, intval(limit_login_option($var_name)));
 
 
 
 
 
666
  }
667
 
668
 
@@ -670,12 +931,6 @@ function limit_login_sanitize_simple_int($var_name) {
670
  function limit_login_sanitize_variables() {
671
  global $limit_login_options;
672
 
673
- limit_login_sanitize_simple_int('allowed_retries');
674
- limit_login_sanitize_simple_int('lockout_duration');
675
- limit_login_sanitize_simple_int('valid_duration');
676
- limit_login_sanitize_simple_int('allowed_lockouts');
677
- limit_login_sanitize_simple_int('long_duration');
678
-
679
  $notify_email_after = max(1, intval(limit_login_option('notify_email_after')));
680
  $limit_login_options['notify_email_after'] = min(limit_login_option('allowed_lockouts'), $notify_email_after);
681
 
@@ -701,12 +956,23 @@ function limit_login_sanitize_variables() {
701
  }
702
 
703
 
 
 
 
 
704
  /* Add admin options page */
705
  function limit_login_admin_menu() {
706
  add_options_page('Limit Login Attempts', 'Limit Login Attempts', 8, 'limit-login-attempts', 'limit_login_option_page');
707
  }
708
 
709
 
 
 
 
 
 
 
 
710
  /* Show log on admin page */
711
  function limit_login_show_log($log) {
712
  if (!is_array($log) || count($log) == 0) {
@@ -730,6 +996,144 @@ function limit_login_show_log($log) {
730
  }
731
  }
732
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
733
  /* Actual admin page */
734
  function limit_login_option_page() {
735
  limit_login_cleanup();
@@ -766,14 +1170,7 @@ function limit_login_option_page() {
766
  if (isset($_POST['update_options'])) {
767
  global $limit_login_options;
768
 
769
- $limit_login_options['client_type'] = $_POST['client_type'];
770
- $limit_login_options['allowed_retries'] = $_POST['allowed_retries'];
771
- $limit_login_options['lockout_duration'] = $_POST['lockout_duration'] * 60;
772
- $limit_login_options['valid_duration'] = $_POST['valid_duration'] * 3600;
773
- $limit_login_options['allowed_lockouts'] = $_POST['allowed_lockouts'];
774
- $limit_login_options['long_duration'] = $_POST['long_duration'] * 3600;
775
- $limit_login_options['notify_email_after'] = $_POST['email_after'];
776
- $limit_login_options['cookies'] = (isset($_POST['cookies']) && $_POST['cookies'] == '1');
777
 
778
  $v = array();
779
  if (isset($_POST['lockout_notify_log'])) {
@@ -805,7 +1202,6 @@ function limit_login_option_page() {
805
  $cookies_note = '';
806
  }
807
  $cookies_yes = limit_login_option('cookies') ? ' checked ' : '';
808
- $cookies_no = limit_login_option('cookies') ? '' : ' checked ';
809
 
810
  $client_type = limit_login_option('client_type');
811
  $client_type_direct = $client_type == LIMIT_LOGIN_DIRECT_ADDR ? ' checked ' : '';
@@ -830,7 +1226,37 @@ function limit_login_option_page() {
830
  $v = explode(',', limit_login_option('lockout_notify'));
831
  $log_checked = in_array('log', $v) ? ' checked ' : '';
832
  $email_checked = in_array('email', $v) ? ' checked ' : '';
 
 
 
 
 
 
833
  ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
834
  <div class="wrap">
835
  <h2><?php echo __('Limit Login Attempts Settings','limit-login-attempts'); ?></h2>
836
  <h3><?php echo __('Statistics','limit-login-attempts'); ?></h3>
@@ -868,6 +1294,13 @@ function limit_login_option_page() {
868
  <input type="text" size="3" maxlength="4" value="<?php echo(limit_login_option('valid_duration')/3600); ?>" name="valid_duration" /> <?php echo __('hours until retries are reset','limit-login-attempts'); ?>
869
  </td>
870
  </tr>
 
 
 
 
 
 
 
871
  <tr>
872
  <th scope="row" valign="top"><?php echo __('Site connection','limit-login-attempts'); ?></th>
873
  <td>
@@ -886,17 +1319,24 @@ function limit_login_option_page() {
886
  </td>
887
  </tr>
888
  <tr>
889
- <th scope="row" valign="top"><?php echo __('Handle cookie login','limit-login-attempts'); ?></th>
890
  <td>
891
- <label><input type="radio" name="cookies" <?php echo $cookies_disabled . $cookies_yes; ?> value="1" /> <?php echo __('Yes','limit-login-attempts'); ?></label> <label><input type="radio" name="cookies" <?php echo $cookies_disabled . $cookies_no; ?> value="0" /> <?php echo __('No','limit-login-attempts'); ?></label>
892
- <?php echo $cookies_note ?>
893
  </td>
894
  </tr>
895
  <tr>
896
- <th scope="row" valign="top"><?php echo __('Notify on lockout','limit-login-attempts'); ?></th>
897
  <td>
898
- <input type="checkbox" name="lockout_notify_log" <?php echo $log_checked; ?> value="log" /> <?php echo __('Log IP','limit-login-attempts'); ?><br />
899
- <input type="checkbox" name="lockout_notify_email" <?php echo $email_checked; ?> value="email" /> <?php echo __('Email to admin after','limit-login-attempts'); ?> <input type="text" size="3" maxlength="4" value="<?php echo(limit_login_option('notify_email_after')); ?>" name="email_after" /> <?php echo __('lockouts','limit-login-attempts'); ?>
 
 
 
 
 
 
 
900
  </td>
901
  </tr>
902
  </table>
@@ -904,42 +1344,30 @@ function limit_login_option_page() {
904
  <input name="update_options" value="<?php echo __('Change Options','limit-login-attempts'); ?>" type="submit" />
905
  </p>
906
  </form>
 
 
 
 
907
  <?php
908
  $log = get_option('limit_login_logged');
909
 
910
  if (is_array($log) && count($log) > 0) {
911
  ?>
912
  <h3><?php echo __('Lockout log','limit-login-attempts'); ?></h3>
 
 
 
 
 
913
  <form action="options-general.php?page=limit-login-attempts" method="post">
914
  <input type="hidden" value="true" name="clear_log" />
915
  <p class="submit">
916
  <input name="submit" value="<?php echo __('Clear Log','limit-login-attempts'); ?>" type="submit" />
917
  </p>
918
  </form>
919
- <style type="text/css" media="screen">
920
- .limit-login-log th {
921
- font-weight: bold;
922
- }
923
- .limit-login-log td, .limit-login-log th {
924
- padding: 1px 5px 1px 5px;
925
- }
926
- td.limit-login-ip {
927
- font-family: "Courier New", Courier, monospace;
928
- vertical-align: top;
929
- }
930
- td.limit-login-max {
931
- width: 100%;
932
- }
933
- </style>
934
- <div class="limit-login-log">
935
- <table class="form-table">
936
- <?php limit_login_show_log($log); ?>
937
- </table>
938
- </div>
939
  <?php
940
  } /* if showing $log */
941
  ?>
942
-
943
  </div>
944
  <?php
945
  }
5
  Description: Limit rate of login attempts, including by way of cookies, for each IP.
6
  Author: Johan Eenfeldt
7
  Author URI: http://devel.kostdoktorn.se
8
+ Version: 2.0beta1
9
 
10
  Copyright 2008, 2009 Johan Eenfeldt
11
 
76
 
77
  /* If notify by email, do so after this number of lockouts */
78
  , 'notify_email_after' => 4
79
+
80
+ /* Enforce limit on new user registrations for IP */
81
+ , 'register_enforce' => false
82
+
83
+ /* Allow this many new user registrations ... */
84
+ , 'register_allowed' => 3
85
+
86
+ /* ... during this time */
87
+ , 'register_duration' => 86400 // 24 hours
88
+
89
+ /* Allow password reset using login name? */
90
+ , 'disable_pwd_reset_username' => false
91
+
92
+ /* ... for capability level_xx or higher */
93
+ , 'pwd_reset_username_limit' => 1
94
+
95
+ /* Allow password resets at all? */
96
+ , 'disable_pwd_reset' => true
97
+
98
+ /* ... for capability level_xx or higher */
99
+ , 'pwd_reset_limit' => 1
100
  );
101
 
102
  $limit_login_my_error_shown = false; /* have we shown our stuff? */
103
  $limit_login_just_lockedout = false; /* started this pageload??? */
104
  $limit_login_nonempty_credentials = false; /* user and pwd nonempty */
105
 
106
+ /* Level of the different roles. Used for descriptive purposes only */
107
+ $limit_login_level_role =
108
+ array(0 => 'Subscriber', 1 => 'Contributor', 2 => 'Author', 7 => 'Editor'
109
+ , 10 => 'Administrator');
110
+
111
 
112
  /*
113
  * Startup
139
  add_action('login_head', 'limit_login_add_error_message');
140
  add_action('login_errors', 'limit_login_fixup_error_messages');
141
  add_action('admin_menu', 'limit_login_admin_menu');
142
+ if (limit_login_option('register_enforce')) {
143
+ add_filter('registration_errors', 'limit_login_filter_registration');
144
+ add_filter('login_message', 'limit_login_filter_login_message');
145
+ }
146
+ if (limit_login_option('disable_pwd_reset') || limit_login_option('disable_pwd_reset_username')) {
147
+ add_filter('allow_password_reset', 'limit_login_filter_pwd_reset', 10, 2);
 
 
 
 
 
148
  }
149
  }
150
 
193
  }
194
 
195
 
196
+ /* Check if it is ok to register new user */
197
+ function is_limit_login_reg_ok() {
198
+ if (!limit_login_option('register_enforce')) {
199
+ return true;
200
+ }
201
+
202
+ $ip = limit_login_get_address();
203
+
204
+ /* too many registrations? */
205
+ $regs = get_option('limit_login_registrations');
206
+ $valid = get_option('limit_login_registrations_valid');
207
+ return (!is_array($regs) || !isset($regs[$ip])
208
+ || !is_array($valid) || !isset($valid[$ip])
209
+ || time() >= $valid[$ip]
210
+ || $regs[$ip] < limit_login_option('register_allowed'));
211
+ }
212
+
213
+
214
  /* Filter: allow login attempt? (called from wp_authenticate()) */
215
  function limit_login_wp_authenticate_user($user, $password) {
216
  if (is_wp_error($user) || is_limit_login_ok() ) {
364
  /* remove retries that are no longer valid */
365
  $valid = !is_null($valid) ? $valid : get_option('limit_login_retries_valid');
366
  $retries = !is_null($retries) ? $retries : get_option('limit_login_retries');
367
+ if (is_array($valid) && !empty($valid) && is_array($retries) && !empty($retries)) {
368
+ foreach ($valid as $ip => $lockout) {
369
+ if ($lockout < $now) {
370
+ unset($valid[$ip]);
371
+ unset($retries[$ip]);
372
+ }
373
+ }
374
+
375
+ /* go through retries directly, if for some reason they've gone out of sync */
376
+ foreach ($retries as $ip => $retry) {
377
+ if (!isset($valid[$ip])) {
378
+ unset($retries[$ip]);
379
+ }
380
+ }
381
+
382
+ update_option('limit_login_retries', $retries);
383
+ update_option('limit_login_retries_valid', $valid);
384
+ }
385
+
386
+ /* do the same for the registration arrays, if necessary */
387
+ $valid = get_option('limit_login_registrations_valid');
388
+ $retries = get_option('limit_login_registrations');
389
+ if (is_array($valid) && !empty($valid) && is_array($retries) && !empty($retries)) {
390
+ foreach ($valid as $ip => $lockout) {
391
+ if ($lockout < $now) {
392
+ unset($valid[$ip]);
393
+ unset($retries[$ip]);
394
+ }
395
+ }
396
+
397
+ /* go through retries directly, if for some reason they've gone out of sync */
398
+ foreach ($retries as $ip => $retry) {
399
+ if (!isset($valid[$ip])) {
400
+ unset($retries[$ip]);
401
+ }
402
+ }
403
+
404
+ update_option('limit_login_registrations', $retries);
405
+ update_option('limit_login_registrations_valid', $valid);
406
+ }
407
+ }
408
+
409
+ /*
410
+ * Handle bookkeeping when new user is registered
411
+ *
412
+ * Increase nr of registrations and reset valid value.
413
+ */
414
+ function limit_login_reg_add() {
415
+ if (!limit_login_option('register_enforce')) {
416
  return;
417
  }
418
 
419
+ $ip = limit_login_get_address();
420
+
421
+ /* Get the arrays with registrations and valid information */
422
+ $regs = get_option('limit_login_registrations');
423
+ $valid = get_option('limit_login_registrations_valid');
424
+ if ($regs === false) {
425
+ $regs = array();
426
+ add_option('limit_login_registrations', $regs, '', 'no');
427
+ }
428
+ if ($valid === false) {
429
+ $valid = array();
430
+ add_option('limit_login_registrations_valid', $valid, '', 'no');
431
+ }
432
+
433
+ /* Check validity and add one registration */
434
+ if (isset($regs[$ip]) && isset($valid[$ip]) && time() < $valid[$ip]) {
435
+ $regs[$ip] ++;
436
+ } else {
437
+ $regs[$ip] = 1;
438
+ }
439
+ $valid[$ip] = time() + limit_login_option('register_duration');
440
+
441
+ update_option('limit_login_registrations', $regs);
442
+ update_option('limit_login_registrations_valid', $valid);
443
+
444
+ /* increase statistics? */
445
+ if ($regs[$ip] >= limit_login_option('register_allowed')) {
446
+ $total = get_option('limit_login_reg_lockouts_total');
447
+ if ($total === false) {
448
+ add_option('limit_login_reg_lockouts_total', 1, '', 'no');
449
+ } else {
450
+ update_option('limit_login_reg_lockouts_total', $total + 1);
451
  }
452
  }
453
 
454
+ /* do housecleaning */
455
+ limit_login_cleanup();
456
+ }
457
+
458
+
459
+ /*
460
+ * Filter: check if new registration is allowed, and filter error messages
461
+ * to remove possibility to brute force user login
462
+ */
463
+ function limit_login_filter_registration($errors) {
464
+ global $limit_login_my_error_shown;
465
+
466
+ $limit_login_my_error_shown = true;
467
+
468
+ if (!is_limit_login_reg_ok()) {
469
+ $errors = new WP_Error();
470
+ $errors->add('lockout', limit_login_reg_error_msg());
471
+ return $errors;
472
+ }
473
+
474
+ /*
475
+ * Not locked out. Now enforce error msg filter and, count attempt if there
476
+ * are no errors.
477
+ */
478
+
479
+ if (!is_wp_error($errors)) {
480
+ limit_login_reg_add();
481
+ return $errors;
482
+ }
483
+
484
+ $codes = $errors->get_error_codes();
485
+
486
+ if (count($codes) <= 1) {
487
+ if (count($codes) == 0) {
488
+ limit_login_reg_add();
489
+ }
490
+ return $errors;
491
+ }
492
+
493
+ /*
494
+ * If more than one error message (meaning both login and email was
495
+ * invalid) we strip any 'username_exists' message.
496
+ *
497
+ * This is to stop someone from trying different usernames with a known
498
+ * bad / empty email address.
499
+ */
500
+
501
+ $key = array_search('username_exists', $codes);
502
+
503
+ if ($key !== false) {
504
+ unset($codes[$key]);
505
+
506
+ $old_errors = $errors;
507
+ $errors = new WP_Error();
508
+ foreach ($codes as $key => $code) {
509
+ $errors->add($code, $old_errors->get_error_message($code));
510
  }
511
  }
512
 
513
+ return $errors;
514
+ }
515
+
516
+
517
+ /* Check if user have level capability */
518
+ function limit_login_user_has_level($userid, $level) {
519
+ $userid = intval($userid);
520
+ $level = intval($level);
521
+
522
+ if ($userid == 0) {
523
+ return false;
524
+ }
525
+
526
+ $user = new WP_User($userid);
527
+
528
+ return ($user && $user->has_cap($level));
529
  }
530
 
531
 
532
+ /* Filter: enforce that password reset is allowed */
533
+ function limit_login_filter_pwd_reset($b, $userid) {
534
+ $limit = null;
535
+
536
+ /* What limit to use, if any */
537
+ if (limit_login_option('disable_pwd_reset')) {
538
+ $limit = intval(limit_login_option('pwd_reset_limit'));
539
+ }
540
+
541
+ if (limit_login_option('disable_pwd_reset_username') && !strpos($_POST['user_login'], '@')) {
542
+ $limit2 = intval(limit_login_option('pwd_reset_username_limit'));
543
+
544
+ if (is_null($limit) || $limit > $limit2) {
545
+ $limit = $limit2;
546
+ }
547
+ }
548
+
549
+ if (is_null($limit)) {
550
+ /* Current reset not limited */
551
+ return $b;
552
+ }
553
+
554
+ /* Test if user have this level */
555
+ if (!limit_login_user_has_level($userid, $limit)) {
556
+ return $b;
557
+ }
558
+
559
+ /* Not allowed -- use same error as retrieve_password() */
560
+ $error = new WP_Error();
561
+ $error->add('invalidcombo', __('<strong>ERROR</strong>: Invalid username or e-mail.'));
562
+ return $error;
563
+ }
564
+
565
+
566
+ /*
567
+ * Notification functions
568
+ */
569
+
570
  /* Email notification of lockout to admin (if configured) */
571
  function limit_login_notify_email($user) {
572
  $ip = limit_login_get_address();
658
  }
659
 
660
 
661
+ /*
662
+ * Handle (och filter) messages and errors shown
663
+ */
664
+
665
+ /* Construct message for registration lockout */
666
+ function limit_login_reg_error_msg() {
667
+ $msg = __('<strong>ERROR</strong>: Too many new user registrations.', 'limit-login-attempts') . ' ';
668
+ return limit_login_error_msg('limit_login_registrations_valid', $msg);
669
+ }
670
+
671
+
672
+ /* Filter: remove other registration error messages */
673
+ function limit_login_filter_login_message($content) {
674
+ if (is_limit_login_reg_page() && !is_limit_login_reg_ok()) {
675
+ return '';
676
+ }
677
+
678
+ return $content;
679
+ }
680
+
681
+
682
  /* Construct informative error message */
683
+ function limit_login_error_msg($lockout_option = 'limit_login_lockouts', $msg = '') {
684
  $ip = limit_login_get_address();
685
+ $lockouts = get_option($lockout_option);
686
 
687
+ if ($msg == '') {
688
+ $msg = __('<strong>ERROR</strong>: Too many failed login attempts.', 'limit-login-attempts') . ' ';
689
+ }
690
 
691
  if (!is_array($lockouts) || !isset($lockouts[$ip]) || time() >= $lockouts[$ip]) {
692
+ /* Huh? No lockout? */
693
+ $msg .= __('Please try again later.', 'limit-login-attempts');
694
  return $msg;
695
  }
696
 
757
  }
758
 
759
 
760
+ /* Should we show errors and messages on this page? */
761
+ function is_limit_login_reg_page() {
762
+ if (isset($_GET['key'])) {
763
+ /* reset password */
764
+ return false;
765
+ }
766
+
767
+ $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : '';
768
+
769
+ return ( $action == 'register' );
770
+ }
771
+
772
+
773
  /* Fix up the error message before showing it */
774
  function limit_login_fixup_error_messages($content) {
775
  global $limit_login_just_lockedout, $limit_login_nonempty_credentials, $limit_login_my_error_shown;
830
  function limit_login_add_error_message() {
831
  global $error, $limit_login_my_error_shown;
832
 
833
+ if (is_limit_login_reg_page() && !is_limit_login_reg_ok()
834
+ && !$limit_login_my_error_shown) {
835
+ $error = limit_login_reg_error_msg();
836
+ return;
837
+ }
838
+
839
  if (!should_limit_login_show_msg() || $limit_login_my_error_shown) {
840
  return;
841
  }
859
  }
860
 
861
 
 
 
 
 
862
  /* Does wordpress version support cookie option? */
863
  function limit_login_support_cookie_option() {
864
  global $wp_version;
866
  }
867
 
868
 
869
+ /*
870
+ * Handle plugin options
871
+ */
872
+
873
+ /* Get current option value */
874
+ function limit_login_option($option_name) {
875
+ global $limit_login_options;
876
+
877
+ if (isset($limit_login_options[$option_name])) {
878
+ return $limit_login_options[$option_name];
879
+ } else {
880
+ return null;
881
+ }
882
  }
883
 
884
 
889
  if ($a !== false) {
890
  global $limit_login_options;
891
 
892
+ if (is_bool($limit_login_options[$var_name])) {
893
+ $a = !!$a;
894
+ } elseif (is_numeric($limit_login_options[$var_name])) {
895
+ $a = intval($a);
896
+ } else {
897
+ $a = (string) $a;
898
+ }
899
+
900
  $limit_login_options[$var_name] = $a;
901
  }
902
  }
904
 
905
  /* Setup global variables from options */
906
  function limit_login_setup_options() {
907
+ global $limit_login_options;
908
+
909
+ foreach ($limit_login_options as $name => $value) {
910
+ limit_login_get_option('limit_login_' . $name, $name);
911
+ }
 
 
 
 
912
 
913
  limit_login_sanitize_variables();
914
  }
916
 
917
  /* Update options in db from global variables */
918
  function limit_login_update_options() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
919
  global $limit_login_options;
920
 
921
+ foreach ($limit_login_options as $name => $value) {
922
+ if (is_bool($value)) {
923
+ $value = $value ? '1' : '0';
924
+ }
925
+ update_option('limit_login_' . $name, $value);
926
+ }
927
  }
928
 
929
 
931
  function limit_login_sanitize_variables() {
932
  global $limit_login_options;
933
 
 
 
 
 
 
 
934
  $notify_email_after = max(1, intval(limit_login_option('notify_email_after')));
935
  $limit_login_options['notify_email_after'] = min(limit_login_option('allowed_lockouts'), $notify_email_after);
936
 
956
  }
957
 
958
 
959
+ /*
960
+ * Admin page stuff
961
+ */
962
+
963
  /* Add admin options page */
964
  function limit_login_admin_menu() {
965
  add_options_page('Limit Login Attempts', 'Limit Login Attempts', 8, 'limit-login-attempts', 'limit_login_option_page');
966
  }
967
 
968
 
969
+ /* Make a guess if we are behind a proxy or not */
970
+ function limit_login_guess_proxy() {
971
+ return isset($_SERVER[LIMIT_LOGIN_PROXY_ADDR])
972
+ ? LIMIT_LOGIN_PROXY_ADDR : LIMIT_LOGIN_DIRECT_ADDR;
973
+ }
974
+
975
+
976
  /* Show log on admin page */
977
  function limit_login_show_log($log) {
978
  if (!is_array($log) || count($log) == 0) {
996
  }
997
  }
998
 
999
+
1000
+ /* Show privileged users various names, and warn if equal to login name */
1001
+ function limit_login_show_users() {
1002
+ global $wpdb;
1003
+
1004
+ $sql = "SELECT u.ID, u.user_login, u.user_nicename, u.display_name"
1005
+ . " , um.meta_value AS role, um2.meta_value AS nickname"
1006
+ . " FROM $wpdb->users u"
1007
+ . " INNER JOIN $wpdb->usermeta um ON u.ID = um.user_id"
1008
+ . " LEFT JOIN $wpdb->usermeta um2 ON u.ID = um2.user_id"
1009
+ . " WHERE um.meta_key = '{$wpdb->prefix}capabilities'"
1010
+ . " AND NOT um.meta_value LIKE '%subscriber%'"
1011
+ . " AND um2.meta_key = 'nickname'";
1012
+
1013
+ $users = $wpdb->get_results($sql);
1014
+
1015
+ if (!$users || count($users) == 0) {
1016
+ return;
1017
+ }
1018
+
1019
+ $r = '';
1020
+ foreach ($users as $user) {
1021
+ $login_ok = strcasecmp($user->user_login, 'admin');
1022
+ $display_ok = strcasecmp($user->user_login, $user->display_name);
1023
+ $nicename_ok = strcasecmp($user->user_login, $user->user_nicename);
1024
+ $nickname_ok = strcasecmp($user->user_login, $user->nickname);
1025
+
1026
+ if ($login_ok && $display_ok && $nicename_ok && $nickname_ok) {
1027
+ continue;
1028
+ }
1029
+
1030
+ $role = implode(',', array_keys(maybe_unserialize($user->role)));
1031
+ $login = limit_login_show_maybe_warning(!$login_ok, $user->user_login
1032
+ , __("Account named admin should not have privileges", 'limit-login-attempts'));
1033
+ $display = limit_login_show_maybe_warning(!$display_ok, $user->display_name
1034
+ , __("Make display name different from login name", 'limit-login-attempts'));
1035
+ $nicename = limit_login_show_maybe_warning(!$nicename_ok, $user->user_nicename
1036
+ , __("Make url name different from login name", 'limit-login-attempts'));
1037
+ $nickname = limit_login_show_maybe_warning(!$nickname_ok, $user->nickname
1038
+ , __("Make nickname different from login name", 'limit-login-attempts'));
1039
+
1040
+ $r .= '<tr><td>' . $login . '</td>'
1041
+ . '<td>' . $role . '</td>'
1042
+ . '<td>' . $display . '</td>'
1043
+ . '<td>' . $nicename . '</td>'
1044
+ . '<td>' . $nickname . '</td>'
1045
+ . '</tr>';
1046
+ }
1047
+
1048
+ if ($r == '') {
1049
+ echo(sprintf('<tr><td>%s</tr></td>'
1050
+ , __("Privileged usernames, display names, url names and nicknames ok", 'limit-login-attempts')));
1051
+ return;
1052
+ }
1053
+
1054
+ echo('<tr>'
1055
+ . '<th scope="col">'
1056
+ . __("User Login", 'limit-login-attempts')
1057
+ . '</th><th scope="col">'
1058
+ . __('Role', 'limit-login-attempts')
1059
+ . '</th><th scope="col">'
1060
+ . __('Display Name', 'limit-login-attempts')
1061
+ . '</th><th scope="col">'
1062
+ . __('URL Name <small>("nicename")</small>', 'limit-login-attempts')
1063
+ . '</th><th scope="col">'
1064
+ . __('Nickname', 'limit-login-attempts')
1065
+ . '</th></tr>'
1066
+ . $r);
1067
+ }
1068
+
1069
+
1070
+ function limit_login_show_maybe_warning($is_warn, $name, $title) {
1071
+ static $alt, $bad_img_url;
1072
+
1073
+ if (!$is_warn) {
1074
+ return $name;
1075
+ }
1076
+
1077
+ if (empty($alt)) {
1078
+ $alt = __("bad name", 'limit-login-attempts');
1079
+ }
1080
+
1081
+ if (empty($bad_img_url)) {
1082
+ if ( !defined('WP_PLUGIN_URL') )
1083
+ $plugin_url = get_option('siteurl') . '/wp-content/plugins';
1084
+ else
1085
+ $plugin_url = WP_PLUGIN_URL;
1086
+
1087
+ $bad_img_url = $plugin_url . '/limit-login-attempts/images/icon_bad.gif';
1088
+ }
1089
+
1090
+ return sprintf('<img src="%s" alt="%s" title="%s" />%s'
1091
+ , $bad_img_url, $alt, $title, $name);
1092
+ }
1093
+
1094
+
1095
+ /* Show all role levels <select> */
1096
+ function limit_login_select_level($current) {
1097
+ global $limit_login_level_role;
1098
+
1099
+ for ($i = 0; $i <= 10; $i++) {
1100
+ $selected = ($i == $current) ? ' SELECTED ' : '';
1101
+ $name = (array_key_exists($i, $limit_login_level_role)) ? ' - ' . $limit_login_level_role[$i] : '';
1102
+ echo("<option value=\"$i\" $selected>$i$name</option>");
1103
+ }
1104
+ }
1105
+
1106
+
1107
+ /* Get most options from $_POST[] (not lockout_notify) */
1108
+ function limit_login_get_options_from_post() {
1109
+ global $limit_login_options;
1110
+
1111
+ $option_multiple =
1112
+ array('lockout_duration' => 60, 'valid_duration' => 3600
1113
+ , 'long_duration' => 3600, 'register_duration' => 3600);
1114
+
1115
+ foreach ($limit_login_options as $name => $oldvalue) {
1116
+ if (is_bool($oldvalue)) {
1117
+ $value = isset($_POST[$name]) && $_POST[$name] == '1';
1118
+ } else {
1119
+ if (!isset($_POST[$name])) {
1120
+ continue;
1121
+ }
1122
+
1123
+ $value = $_POST[$name];
1124
+ if (is_numeric($oldvalue)) {
1125
+ $value = intval($value);
1126
+ }
1127
+ if (array_key_exists($name, $option_multiple)) {
1128
+ $value = $value * $option_multiple[$name];
1129
+ }
1130
+ }
1131
+
1132
+ $limit_login_options[$name] = $value;
1133
+ }
1134
+ }
1135
+
1136
+
1137
  /* Actual admin page */
1138
  function limit_login_option_page() {
1139
  limit_login_cleanup();
1170
  if (isset($_POST['update_options'])) {
1171
  global $limit_login_options;
1172
 
1173
+ limit_login_get_options_from_post();
 
 
 
 
 
 
 
1174
 
1175
  $v = array();
1176
  if (isset($_POST['lockout_notify_log'])) {
1202
  $cookies_note = '';
1203
  }
1204
  $cookies_yes = limit_login_option('cookies') ? ' checked ' : '';
 
1205
 
1206
  $client_type = limit_login_option('client_type');
1207
  $client_type_direct = $client_type == LIMIT_LOGIN_DIRECT_ADDR ? ' checked ' : '';
1226
  $v = explode(',', limit_login_option('lockout_notify'));
1227
  $log_checked = in_array('log', $v) ? ' checked ' : '';
1228
  $email_checked = in_array('email', $v) ? ' checked ' : '';
1229
+
1230
+ $disable_pwd_reset_username_yes = limit_login_option('disable_pwd_reset_username') ? ' checked ' : '';
1231
+ $disable_pwd_reset_yes = limit_login_option('disable_pwd_reset') ? ' checked ' : '';
1232
+
1233
+ $register_enforce_yes = limit_login_option('register_enforce') ? ' checked ' : '';
1234
+
1235
  ?>
1236
+ <style type="text/css" media="screen">
1237
+ table.limit-login {
1238
+ width: 100%;
1239
+ border-collapse: collapse;
1240
+ }
1241
+ .limit-login th {
1242
+ font-size: 12px;
1243
+ font-weight: bold;
1244
+ text-align: left;
1245
+ padding: 0;
1246
+ }
1247
+ .limit-login td {
1248
+ font-size: 11px;
1249
+ line-height: 11px;
1250
+ padding: 1px 5px 1px 0;
1251
+ }
1252
+ td.limit-login-ip {
1253
+ font-family: "Courier New", Courier, monospace;
1254
+ vertical-align: top;
1255
+ }
1256
+ td.limit-login-max {
1257
+ width: 100%;
1258
+ }
1259
+ </style>
1260
  <div class="wrap">
1261
  <h2><?php echo __('Limit Login Attempts Settings','limit-login-attempts'); ?></h2>
1262
  <h3><?php echo __('Statistics','limit-login-attempts'); ?></h3>
1294
  <input type="text" size="3" maxlength="4" value="<?php echo(limit_login_option('valid_duration')/3600); ?>" name="valid_duration" /> <?php echo __('hours until retries are reset','limit-login-attempts'); ?>
1295
  </td>
1296
  </tr>
1297
+ <tr>
1298
+ <th scope="row" valign="top"><?php echo __('User cookie login','limit-login-attempts'); ?></th>
1299
+ <td>
1300
+ <label><input type="checkbox" name="cookies" <?php echo $cookies_disabled . $cookies_yes; ?> value="1" /> <?php echo __('Handle cookie login','limit-login-attempts'); ?></label>
1301
+ <?php echo $cookies_note ?>
1302
+ </td>
1303
+ </tr>
1304
  <tr>
1305
  <th scope="row" valign="top"><?php echo __('Site connection','limit-login-attempts'); ?></th>
1306
  <td>
1319
  </td>
1320
  </tr>
1321
  <tr>
1322
+ <th scope="row" valign="top"><?php echo __('Notify on lockout','limit-login-attempts'); ?></th>
1323
  <td>
1324
+ <input type="checkbox" name="lockout_notify_log" <?php echo $log_checked; ?> value="log" /> <?php echo __('Log IP','limit-login-attempts'); ?><br />
1325
+ <input type="checkbox" name="lockout_notify_email" <?php echo $email_checked; ?> value="email" /> <?php echo __('Email to admin after','limit-login-attempts'); ?> <input type="text" size="3" maxlength="4" value="<?php echo(limit_login_option('notify_email_after')); ?>" name="email_after" /> <?php echo __('lockouts','limit-login-attempts'); ?>
1326
  </td>
1327
  </tr>
1328
  <tr>
1329
+ <th scope="row" valign="top"><?php echo __('Password reset','limit-login-attempts'); ?></th>
1330
  <td>
1331
+ <label><input type="checkbox" name="disable_pwd_reset_username" <?php echo $disable_pwd_reset_username_yes; ?> value="1" /> <?php echo __('Disable password reset using login name for user this level or higher','limit-login-attempts'); ?></label> <select name="pwd_reset_username_limit"><?php limit_login_select_level(limit_login_option('pwd_reset_username_limit')); ?></select>
1332
+ <br />
1333
+ <label><input type="checkbox" name="disable_pwd_reset" <?php echo $disable_pwd_reset_yes; ?> value="1" /> <?php echo __('Disable password reset for users this level or higher','limit-login-attempts'); ?></label> <select name="pwd_reset_limit"><?php limit_login_select_level(limit_login_option('pwd_reset_limit')); ?></select>
1334
+ </td>
1335
+ </tr>
1336
+ <tr>
1337
+ <th scope="row" valign="top"><?php echo __('New user registration','limit-login-attempts'); ?></th>
1338
+ <td>
1339
+ <input type="checkbox" name="register_enforce" <?php echo $register_enforce_yes; ?> value="1" /> <?php echo __('Only allow','limit-login-attempts'); ?> <input type="text" size="3" maxlength="4" value="<?php echo(limit_login_option('register_allowed')); ?>" name="register_allowed" /> <?php echo __('user registrations every','limit-login-attempts'); ?> <input type="text" size="3" maxlength="4" value="<?php echo(limit_login_option('register_duration')/3600); ?>" name="register_duration" /> <?php echo __('hours','limit-login-attempts'); ?>
1340
  </td>
1341
  </tr>
1342
  </table>
1344
  <input name="update_options" value="<?php echo __('Change Options','limit-login-attempts'); ?>" type="submit" />
1345
  </p>
1346
  </form>
1347
+ <h3><?php echo __('Privileged users','limit-login-attempts'); ?></h3>
1348
+ <table class="limit-login">
1349
+ <?php limit_login_show_users(); ?>
1350
+ </table>
1351
  <?php
1352
  $log = get_option('limit_login_logged');
1353
 
1354
  if (is_array($log) && count($log) > 0) {
1355
  ?>
1356
  <h3><?php echo __('Lockout log','limit-login-attempts'); ?></h3>
1357
+ <div class="limit-login">
1358
+ <table>
1359
+ <?php limit_login_show_log($log); ?>
1360
+ </table>
1361
+ </div>
1362
  <form action="options-general.php?page=limit-login-attempts" method="post">
1363
  <input type="hidden" value="true" name="clear_log" />
1364
  <p class="submit">
1365
  <input name="submit" value="<?php echo __('Clear Log','limit-login-attempts'); ?>" type="submit" />
1366
  </p>
1367
  </form>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1368
  <?php
1369
  } /* if showing $log */
1370
  ?>
 
1371
  </div>
1372
  <?php
1373
  }
readme.txt CHANGED
@@ -5,10 +5,12 @@ Requires at least: 2.5
5
  Tested up to: 2.7.1
6
  Stable tag: 1.3.1
7
 
8
- Limit rate of login attempts, including by way of cookies, for each IP.
9
 
10
  == Description ==
11
 
 
 
12
  Limit the number of login attempts possible both through normal login as well as (WordPress 2.7+) using auth cookies.
13
 
14
  By default WordPress allows unlimited login attempts either through the login page or by sending special cookies. This allows passwords (or hashes) to be brute-force cracked with relative ease.
@@ -18,10 +20,12 @@ Limit Login Attempts blocks an Internet address from making further attempts aft
18
  Features
19
 
20
  * Limit the number of retry attempts when logging in (for each IP). Fully customizable
21
- * (WordPress 2.7+) Limit the number of attempts to log in using auth cookies in same way
22
  * Informs user about remaining retries or lockout time on login page
23
  * Optional logging, optional email notification
24
  * Handles server behind reverse proxy
 
 
 
25
 
26
  Plugin uses standard actions and filters only.
27
 
@@ -33,6 +37,12 @@ Plugin uses standard actions and filters only.
33
 
34
  If you have any questions or problems please make a post here: http://wordpress.org/tags/limit-login-attempts
35
 
 
 
 
 
 
 
36
  == Frequently Asked Questions ==
37
 
38
  = What is this option about site connection and reverse proxy? =
@@ -53,6 +63,20 @@ If you have ftp / ssh access to the site rename the file "wp-content/plugins/lim
53
 
54
  If you have access to the database (for example through phpMyAdmin) you can clear the limit_login_lockouts option in the wordpress options table. In a default setup this would work: "UPDATE wp_options SET option_value = '' WHERE option_name = 'limit_login_lockouts'"
55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  == Screenshots ==
57
 
58
  1. Loginscreen after failed login with retries remaining
@@ -62,6 +86,13 @@ If you have access to the database (for example through phpMyAdmin) you can clea
62
 
63
  == Version History ==
64
 
 
 
 
 
 
 
 
65
  * Version 1.3.1
66
  * Added Catalan translation, thanks to Robert Buj
67
  * Added Romanian translation, thanks to Robert Tudor
5
  Tested up to: 2.7.1
6
  Stable tag: 1.3.1
7
 
8
+ Limit rate of login attempts, including by way of cookies, for each IP. (BETA VERSION)
9
 
10
  == Description ==
11
 
12
+ THIS IS A BETA VERSION!
13
+
14
  Limit the number of login attempts possible both through normal login as well as (WordPress 2.7+) using auth cookies.
15
 
16
  By default WordPress allows unlimited login attempts either through the login page or by sending special cookies. This allows passwords (or hashes) to be brute-force cracked with relative ease.
20
  Features
21
 
22
  * Limit the number of retry attempts when logging in (for each IP). Fully customizable
 
23
  * Informs user about remaining retries or lockout time on login page
24
  * Optional logging, optional email notification
25
  * Handles server behind reverse proxy
26
+ * (WordPress 2.7+) Also handles attempts to log in using auth cookies
27
+ * Helps hide user login names
28
+ * Optional restriction on password reset attempts for privileged users, and rate limit new user registration
29
 
30
  Plugin uses standard actions and filters only.
31
 
37
 
38
  If you have any questions or problems please make a post here: http://wordpress.org/tags/limit-login-attempts
39
 
40
+ == Todo ==
41
+
42
+ * There is no built in way to change user login name or nicename.
43
+ * Smarter matching vs login name
44
+ * Translations
45
+
46
  == Frequently Asked Questions ==
47
 
48
  = What is this option about site connection and reverse proxy? =
63
 
64
  If you have access to the database (for example through phpMyAdmin) you can clear the limit_login_lockouts option in the wordpress options table. In a default setup this would work: "UPDATE wp_options SET option_value = '' WHERE option_name = 'limit_login_lockouts'"
65
 
66
+ = Why the privileged users list? Why are some names marked? =
67
+
68
+ These are the various names WordPress has for each user. To increase security the login name should not be the same as any of these.
69
+
70
+ = What is URL Name / "nicename"? =
71
+
72
+ "Nicename" is what WordPress calls it (internally). It is constructed directly from the login name and is used in the public author url (among other things).
73
+
74
+ = I disabled password reset for administrators and forgot my password, what do I do? =
75
+
76
+ If you have ftp / ssh access look at the answer regarding being locked out above.
77
+
78
+ If you have access to the database (for example through phpMyAdmin) you can clear the limit_login_reset_min_role option in the wordpress options table. In a default setup this would work: "UPDATE wp_options SET option_value = '' WHERE option_name = 'limit_login_reset_min_role'"
79
+
80
  == Screenshots ==
81
 
82
  1. Loginscreen after failed login with retries remaining
86
 
87
  == Version History ==
88
 
89
+ * Version 2.0beta1
90
+ * Added a number of options that when activated make it harder to find login names of users
91
+ * disable password reset using username (accept user email only) for users with a specified role or higher
92
+ * disable password reset for users with a specified role or higher
93
+ * restrict rate of new user registrations
94
+ * filter registration error messages to avoid possible way to brute force find user login name
95
+ * list of privileged users show which login names can be discovered from user displayname, nickname or "url name"/nicename
96
  * Version 1.3.1
97
  * Added Catalan translation, thanks to Robert Buj
98
  * Added Romanian translation, thanks to Robert Tudor