Login Security Solution - Version 0.7.0

Version Description

  • The "lost your password" process now validates passwords.
  • Tested under WordPress 3.3.1 regular and 3.4beta2 multisite.
  • Unit tests pass using PHP 5.4.0RC8-dev, 5.3.11-dev, and 5.2.18-dev.
Download this release

Release Info

Developer convissor
Plugin Icon wp plugin Login Security Solution
Version 0.7.0
Comparing to
See all releases

Code changes from version 0.6.1 to 0.7.0

login-security-solution.php CHANGED
@@ -6,7 +6,7 @@
6
  * Description: Requires very strong passwords, repels brute force login attacks, prevents login information disclosures, expires idle sessions, notifies admins of attacks and breaches, permits administrators to disable logins for maintenance or emergency reasons and reset all passwords.
7
  *
8
  * Plugin URI: http://wordpress.org/extend/plugins/login-security-solution/
9
- * Version: 0.6.1
10
  * Author: Daniel Convissor
11
  * Author URI: http://www.analysisandsolutions.com/
12
  * License: GPLv2
@@ -474,6 +474,10 @@ class login_security_solution {
474
  $ours = __('Your password has expired. Please log and change it.', self::ID);
475
  $ours .= ' ' . sprintf(__('We provide a %d minute grace period to do so.', self::ID), $this->options['pw_change_grace_period_minutes']);
476
  break;
 
 
 
 
477
  }
478
  }
479
 
@@ -498,7 +502,7 @@ class login_security_solution {
498
  *
499
  * @param WP_User the user object being edited
500
  * @param string $user_pass the unhashed new password
501
- * @return void
502
  *
503
  * @uses login_security_solution::process_pw_metadata() to update user
504
  * metadata
@@ -507,6 +511,16 @@ class login_security_solution {
507
  if (empty($user->ID)) {
508
  return false;
509
  }
 
 
 
 
 
 
 
 
 
 
510
  $this->process_pw_metadata($user->ID, $user_pass);
511
  }
512
 
6
  * Description: Requires very strong passwords, repels brute force login attacks, prevents login information disclosures, expires idle sessions, notifies admins of attacks and breaches, permits administrators to disable logins for maintenance or emergency reasons and reset all passwords.
7
  *
8
  * Plugin URI: http://wordpress.org/extend/plugins/login-security-solution/
9
+ * Version: 0.7.0
10
  * Author: Daniel Convissor
11
  * Author URI: http://www.analysisandsolutions.com/
12
  * License: GPLv2
474
  $ours = __('Your password has expired. Please log and change it.', self::ID);
475
  $ours .= ' ' . sprintf(__('We provide a %d minute grace period to do so.', self::ID), $this->options['pw_change_grace_period_minutes']);
476
  break;
477
+ case 'pw_reset_bad':
478
+ $ours = __('The password you just created is not secure so must be changed. Use it now to log in then go to your profile page and create a new password.', self::ID);
479
+ $ours .= ' ' . sprintf(__('We provide a %d minute grace period to do so.', self::ID), $this->options['pw_change_grace_period_minutes']);
480
+ break;
481
  }
482
  }
483
 
502
  *
503
  * @param WP_User the user object being edited
504
  * @param string $user_pass the unhashed new password
505
+ * @return mixed return values provided for unit testing
506
  *
507
  * @uses login_security_solution::process_pw_metadata() to update user
508
  * metadata
511
  if (empty($user->ID)) {
512
  return false;
513
  }
514
+
515
+ $user->user_pass = $user_pass;
516
+ if (!$this->validate_pw($user)) {
517
+ $this->process_pw_metadata($user->ID, $user_pass);
518
+ $this->set_pw_force_change($user->ID);
519
+ $this->set_pw_grace_period($user->ID);
520
+ $this->redirect_to_login('pw_reset_bad');
521
+ return -1;
522
+ }
523
+
524
  $this->process_pw_metadata($user->ID, $user_pass);
525
  }
526
 
readme.txt CHANGED
@@ -4,14 +4,16 @@ Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=danie
4
  Tags: login, password, idle, timeout, maintenance, security, attack, hack, lock, ban
5
  Requires at least: 3.0
6
  Tested up to: 3.4beta2
7
- Stable tag: trunk
 
 
 
 
8
 
9
- Repels brute force attacks (by IP, name, password). Requires very strong passwords (complex, don't match site/user info, etc). Plus much more!
10
 
11
  == Description ==
12
 
13
- Locks down login related security matters for regular and multisite
14
- WordPress installations.
15
 
16
  * Blocks brute force and dictionary attacks without inconveniencing
17
  legitimate users or administrators
@@ -67,7 +69,8 @@ The tests have caught every password dictionary entry I've tried.
67
  = Improvements Over Similar WordPress Plugins =
68
 
69
  * Multisite network support
70
- * The plugin itself is secure against SQL, HTML, and header injections
 
71
  * Notice-free code means no information disclosures if `display_errors`
72
  is on and `error_reporting` includes `E_NOTICE`
73
  * Only loads files, actions, and filters needed for enabled options
@@ -104,8 +107,8 @@ clients and friends.
104
 
105
  == Installation ==
106
 
107
- 1. Download the package
108
- from `http://wordpress.org/extend/plugins/login-security-solution/`
109
 
110
  1. Unzip the file.
111
 
@@ -170,7 +173,7 @@ clients and friends.
170
  1. Adjust the settings as desired. This plugin's settings page can be
171
  reached via a sub-menu entry under WordPress' "Settings" menu or this
172
  plugin's entry on WordPress' "Plugins" page. Sites using WordPress'
173
- multisite network capabilitiy will find the "Settings" and "Plugin"
174
  menus under "My Sites | Network Admin".
175
 
176
  1. Run the "Change All Passwords" process. This is necessary to ensure
@@ -232,11 +235,11 @@ administrators from being inconvenienced. Plus it provides a quick sand
232
  trap that ties up attackers' resources instead of immediately tipping them
233
  off that the jig is up.
234
 
235
- = Won't the slowdowns open my website to Denial of Serivice (DOS) attacks? =
236
 
237
  Yeah, the DOS potential is there. I mitigated it for the most part by
238
- disconnecting the database link (the most precious resorce in most
239
- situations) before sleeping. But remember, distributed deinal of service
240
  attacks are fairly easy to initiate these days. If someone really wants to
241
  shut down your site, they'll be able to do it without even touching this
242
  plugin's login failure process.
@@ -252,6 +255,11 @@ then `cd` into that directory and run:
252
 
253
  == Changelog ==
254
 
 
 
 
 
 
255
  = 0.6.1 =
256
  * Minor wording adjustments.
257
 
@@ -301,15 +309,15 @@ problems under PHP 5.4.
301
  * Initial import to `plugins.svn.wordpress.org`.
302
 
303
  = 0.0.3 =
304
- * Fix mixups in the code saving the "Change All Passwords" admin UI.
305
  * Adjust IdleTest so it doesn't radically change `wp_users` auto increment.
306
  * Tested under WordPress 3.3.1.
307
  * Unit tests pass using PHP 5.4.0RC8-dev, 5.3.11-dev, and 5.2.18-dev.
308
 
309
  = 0.0.2 =
310
  * Use Unicode character properties to improve portability.
311
- * Stop tests short if not in a wp install.
312
- * Skip dict test if dict not available.
313
  * Skip database tests if transactions are not available.
314
  * Tested under WordPress 3.3.1.
315
  * Unit tests pass using PHP 5.4.0RC8-dev, 5.3.11-dev, and 5.2.18-dev.
4
  Tags: login, password, idle, timeout, maintenance, security, attack, hack, lock, ban
5
  Requires at least: 3.0
6
  Tested up to: 3.4beta2
7
+ Stable tag: 0.7.0
8
+
9
+ Security against brute force attacks by tracking IP, name, password;
10
+ requiring very strong passwords. Idle timeout. Maintenance mode. Multisite
11
+ ready!
12
 
 
13
 
14
  == Description ==
15
 
16
+ Locks down login security for multisite and regular WordPress installations.
 
17
 
18
  * Blocks brute force and dictionary attacks without inconveniencing
19
  legitimate users or administrators
69
  = Improvements Over Similar WordPress Plugins =
70
 
71
  * Multisite network support
72
+ * Takes security seriously so the plugin itself does not open your site
73
+ to SQL, HTML, or header injection vulnerabilities
74
  * Notice-free code means no information disclosures if `display_errors`
75
  is on and `error_reporting` includes `E_NOTICE`
76
  * Only loads files, actions, and filters needed for enabled options
107
 
108
  == Installation ==
109
 
110
+ 1. Download the Login Security Solution zip file from WordPress' plugin
111
+ site: `http://wordpress.org/extend/plugins/login-security-solution/`
112
 
113
  1. Unzip the file.
114
 
173
  1. Adjust the settings as desired. This plugin's settings page can be
174
  reached via a sub-menu entry under WordPress' "Settings" menu or this
175
  plugin's entry on WordPress' "Plugins" page. Sites using WordPress'
176
+ multisite network capability will find the "Settings" and "Plugin"
177
  menus under "My Sites | Network Admin".
178
 
179
  1. Run the "Change All Passwords" process. This is necessary to ensure
235
  trap that ties up attackers' resources instead of immediately tipping them
236
  off that the jig is up.
237
 
238
+ = Won't the slowdowns open my website to Denial of Service (DOS) attacks? =
239
 
240
  Yeah, the DOS potential is there. I mitigated it for the most part by
241
+ disconnecting the database link (the most precious resource in most
242
+ situations) before sleeping. But remember, distributed denial of service
243
  attacks are fairly easy to initiate these days. If someone really wants to
244
  shut down your site, they'll be able to do it without even touching this
245
  plugin's login failure process.
255
 
256
  == Changelog ==
257
 
258
+ = 0.7.0 =
259
+ * The "lost your password" process now validates passwords.
260
+ * Tested under WordPress 3.3.1 regular and 3.4beta2 multisite.
261
+ * Unit tests pass using PHP 5.4.0RC8-dev, 5.3.11-dev, and 5.2.18-dev.
262
+
263
  = 0.6.1 =
264
  * Minor wording adjustments.
265
 
309
  * Initial import to `plugins.svn.wordpress.org`.
310
 
311
  = 0.0.3 =
312
+ * Fix mix ups in the code saving the "Change All Passwords" admin UI.
313
  * Adjust IdleTest so it doesn't radically change `wp_users` auto increment.
314
  * Tested under WordPress 3.3.1.
315
  * Unit tests pass using PHP 5.4.0RC8-dev, 5.3.11-dev, and 5.2.18-dev.
316
 
317
  = 0.0.2 =
318
  * Use Unicode character properties to improve portability.
319
+ * Stop tests short if not in a WordPress install.
320
+ * Skip `dict` test if `dict` not available.
321
  * Skip database tests if transactions are not available.
322
  * Tested under WordPress 3.3.1.
323
  * Unit tests pass using PHP 5.4.0RC8-dev, 5.3.11-dev, and 5.2.18-dev.
tests/LoginMessageTest.php CHANGED
@@ -58,7 +58,12 @@ class LoginMessageTest extends TestCase {
58
  public function test_login_message__idle() {
59
  $_GET[self::$lss->key_login_msg] = 'idle';
60
 
61
- $ours = sprintf(__('It has been over %d minutes since your last action.', self::ID), self::$lss->options['idle_timeout']);
 
 
 
 
 
62
  $ours .= ' ' . __('Please log back in.', self::ID);
63
 
64
  $actual = self::$lss->login_message('input');
@@ -91,8 +96,29 @@ class LoginMessageTest extends TestCase {
91
  public function test_login_message__pw_grace() {
92
  $_GET[self::$lss->key_login_msg] = 'pw_grace';
93
 
 
 
 
 
 
94
  $ours = __('Your password has expired. Please log and change it.', self::ID);
95
- $ours .= ' ' . sprintf(__('We provide a %d minute grace period to do so.', self::ID), self::$lss->options['pw_change_grace_period_minutes']);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
 
97
  $actual = self::$lss->login_message('input');
98
  $this->assertEquals('input' . $this->ours($ours), $actual,
58
  public function test_login_message__idle() {
59
  $_GET[self::$lss->key_login_msg] = 'idle';
60
 
61
+ $value = 8;
62
+ $options = self::$lss->options;
63
+ $options['idle_timeout'] = $value;
64
+ self::$lss->options = $options;
65
+
66
+ $ours = sprintf(__('It has been over %d minutes since your last action.', self::ID), $value);
67
  $ours .= ' ' . __('Please log back in.', self::ID);
68
 
69
  $actual = self::$lss->login_message('input');
96
  public function test_login_message__pw_grace() {
97
  $_GET[self::$lss->key_login_msg] = 'pw_grace';
98
 
99
+ $value = 8;
100
+ $options = self::$lss->options;
101
+ $options['pw_change_grace_period_minutes'] = $value;
102
+ self::$lss->options = $options;
103
+
104
  $ours = __('Your password has expired. Please log and change it.', self::ID);
105
+ $ours .= ' ' . sprintf(__('We provide a %d minute grace period to do so.', self::ID), $value);
106
+
107
+ $actual = self::$lss->login_message('input');
108
+ $this->assertEquals('input' . $this->ours($ours), $actual,
109
+ 'Output should have been modified.');
110
+ }
111
+
112
+ public function test_login_message__pw_reset_bad() {
113
+ $_GET[self::$lss->key_login_msg] = 'pw_reset_bad';
114
+
115
+ $value = 8;
116
+ $options = self::$lss->options;
117
+ $options['pw_change_grace_period_minutes'] = $value;
118
+ self::$lss->options = $options;
119
+
120
+ $ours = __('The password you just created is not secure so must be changed. Use it now to log in then go to your profile page and create a new password.', self::ID);
121
+ $ours .= ' ' . sprintf(__('We provide a %d minute grace period to do so.', self::ID), $value);
122
 
123
  $actual = self::$lss->login_message('input');
124
  $this->assertEquals('input' . $this->ours($ours), $actual,
tests/PasswordChangeTest.php CHANGED
@@ -188,7 +188,7 @@ class PasswordChangeTest extends TestCase {
188
 
189
  // Check the outcome.
190
  $actual = self::$lss->get_pw_changed_time($this->user->ID);
191
- $this->assertGreaterThan(0, $actual, 'Changed time should be 0.');
192
 
193
  $actual = self::$lss->is_pw_reused(self::$pass_1, $this->user->ID);
194
  $this->assertTrue($actual, 'Password should show up as reused');
@@ -198,6 +198,37 @@ class PasswordChangeTest extends TestCase {
198
  $wpdb->query('ROLLBACK TO empty');
199
  }
200
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
  /*
202
  * PROFILE UPDATE
203
  */
188
 
189
  // Check the outcome.
190
  $actual = self::$lss->get_pw_changed_time($this->user->ID);
191
+ $this->assertGreaterThan(0, $actual, 'Changed time should be > 0.');
192
 
193
  $actual = self::$lss->is_pw_reused(self::$pass_1, $this->user->ID);
194
  $this->assertTrue($actual, 'Password should show up as reused');
198
  $wpdb->query('ROLLBACK TO empty');
199
  }
200
 
201
+ public function test_password_reset__bad_pw() {
202
+ global $wpdb;
203
+
204
+ $bad_pw = 'too simple';
205
+
206
+ $expected_error = 'Cannot modify header information';
207
+ $this->expected_errors($expected_error);
208
+ self::$location_expected = get_option('siteurl')
209
+ . '/wp-login.php?action=login&'
210
+ . self::$lss->key_login_msg . '=pw_reset_bad';
211
+
212
+ $actual = self::$lss->password_reset($this->user, $bad_pw);
213
+ $this->assertEquals(-1, $actual, 'password_reset() return.');
214
+
215
+ // Check the outcome.
216
+ $actual = self::$lss->get_pw_changed_time($this->user->ID);
217
+ $this->assertGreaterThan(0, $actual, 'Changed time should be > 0.');
218
+
219
+ $actual = self::$lss->is_pw_reused($bad_pw, $this->user->ID);
220
+ $this->assertTrue($actual, 'Password should show up as reused');
221
+
222
+ $this->ensure_grace_and_force_are_populated();
223
+
224
+ $wpdb->query('ROLLBACK TO empty');
225
+
226
+ $this->assertTrue($this->were_expected_errors_found(),
227
+ "Expected error not found: '$expected_error'");
228
+ $this->assertEquals(self::$location_expected, self::$location_actual,
229
+ 'wp_redirect() produced unexpected location header.');
230
+ }
231
+
232
  /*
233
  * PROFILE UPDATE
234
  */