Login Security Solution - Version 0.5.0

Version Description

  • Have multisite network mode use the saved options instead of the defaults.
  • Close more HTML injection vectors. (One would think WordPress' built in functions would already do this. Alas...)
  • Get the success/error messages to work when saving settings via the Network Admin page.
  • Improve unit tests by ensuring the fail table uses InnoDB.
  • 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.5.0
Comparing to
See all releases

Code changes from version 0.4.0 to 0.5.0

Files changed (4) hide show
  1. admin.inc +32 -18
  2. login-security-solution.php +8 -2
  3. readme.txt +19 -4
  4. tests/TestCase.php +9 -3
admin.inc CHANGED
@@ -391,19 +391,19 @@ class login_security_solution_admin extends login_security_solution {
391
 
392
  add_settings_section(
393
  self::ID . '-login',
394
- __('Login Failure Policies', self::ID),
395
  array(&$this, 'section_login'),
396
  self::ID
397
  );
398
  add_settings_section(
399
  self::ID . '-pw',
400
- __('Password Policies', self::ID),
401
  array(&$this, 'section_blank'),
402
  self::ID
403
  );
404
  add_settings_section(
405
  self::ID . '-misc',
406
- __('Miscellaneous Policies', self::ID),
407
  array(&$this, 'section_blank'),
408
  self::ID
409
  );
@@ -412,7 +412,7 @@ class login_security_solution_admin extends login_security_solution {
412
  foreach ($this->fields as $id => $field) {
413
  add_settings_field(
414
  $id,
415
- $field['label'],
416
  array(&$this, $id),
417
  self::ID,
418
  self::ID . '-' . $field['group']
@@ -425,6 +425,13 @@ class login_security_solution_admin extends login_security_solution {
425
  * @return void
426
  */
427
  public function page_settings() {
 
 
 
 
 
 
 
428
  echo '<h2>' . $this->hsc_utf8($this->text_settings) . '</h2>';
429
  echo '<form action="' . $this->hsc_utf8($this->form_action) . '" method="post">' . "\n";
430
  settings_fields($this->option_name);
@@ -532,7 +539,8 @@ class login_security_solution_admin extends login_security_solution {
532
  $out = $this->options_default;
533
  if (!is_array($in)) {
534
  // Not translating this since only hackers will see it.
535
- add_settings_error($this->option_name, $this->option_name,
 
536
  'Input must be an array.');
537
  return $out;
538
  }
@@ -548,7 +556,8 @@ class login_security_solution_admin extends login_security_solution {
548
 
549
  if (!is_scalar($in[$name])) {
550
  // Not translating this since only hackers will see it.
551
- add_settings_error($this->option_name, $name,
 
552
  $this->hsc_utf8("'" . $field['label'])
553
  . "' was not a scalar, $default");
554
  continue;
@@ -558,7 +567,8 @@ class login_security_solution_admin extends login_security_solution {
558
  case 'bool':
559
  if ($in[$name] != 0 && $in[$name] != 1) {
560
  // Not translating this since only hackers will see it.
561
- add_settings_error($this->option_name, $name,
 
562
  $this->hsc_utf8("'" . $field['label']
563
  . "' must be '0' or '1', $default"));
564
  continue 2;
@@ -566,7 +576,8 @@ class login_security_solution_admin extends login_security_solution {
566
  break;
567
  case 'int':
568
  if (!ctype_digit($in[$name])) {
569
- add_settings_error($this->option_name, $name,
 
570
  $this->hsc_utf8("'" . $field['label'] . "' "
571
  . __("must be an integer,", self::ID)
572
  . ' ' . $default));
@@ -575,7 +586,8 @@ class login_security_solution_admin extends login_security_solution {
575
  if (array_key_exists('greater_than', $field)
576
  && $in[$name] < $field['greater_than'])
577
  {
578
- add_settings_error($this->option_name, $name,
 
579
  $this->hsc_utf8("'" . $field['label'] . "' "
580
  . sprintf($gt_format, $field['greater_than'])
581
  . ' ' . $default));
@@ -589,7 +601,8 @@ class login_security_solution_admin extends login_security_solution {
589
  // Special check to make sure Delay Tier 3 > Delay Tier 2.
590
  $name = 'login_fail_tier_3';
591
  if ($out[$name] <= $out['login_fail_tier_2']) {
592
- add_settings_error($this->option_name, $name,
 
593
  $this->hsc_utf8("'" . $this->fields[$name]['label'] . "' "
594
  . sprintf($gt_format, $this->fields['login_fail_tier_2']['label'])
595
  . ' ' . $default));
@@ -600,7 +613,8 @@ class login_security_solution_admin extends login_security_solution {
600
  // Speical check to ensure reuse count is set if aging is enabled.
601
  $name = 'pw_reuse_count';
602
  if ($out['pw_change_days'] && !$out[$name]) {
603
- add_settings_error($this->option_name, $name,
 
604
  $this->hsc_utf8("'" . $this->fields[$name]['label'] . "' "
605
  . sprintf($gt_format, 1)
606
  . ' ' . $default));
@@ -747,16 +761,16 @@ class login_security_solution_admin extends login_security_solution {
747
  case $this->text_button_remind:
748
  if (!empty($in[$this->key_checkbox_require])) {
749
  add_settings_error($this->option_pw_force_change_name,
750
- $this->option_pw_force_change_name,
751
  $crossed);
752
  } elseif (empty($in[$this->key_checkbox_remind])) {
753
  add_settings_error($this->option_pw_force_change_name,
754
- $this->option_pw_force_change_name,
755
  $this->hsc_utf8(sprintf($confirm, "No thanks")));
756
  } else {
757
  // Translaton already in WP.
758
  add_settings_error($this->option_pw_force_change_name,
759
- $this->option_pw_force_change_name,
760
  $this->hsc_utf8(__("Success!")),
761
  'updated');
762
  $out = 1;
@@ -765,23 +779,23 @@ class login_security_solution_admin extends login_security_solution {
765
  case $this->text_button_require:
766
  if (!empty($in[$this->key_checkbox_remind])) {
767
  add_settings_error($this->option_pw_force_change_name,
768
- $this->option_pw_force_change_name,
769
  $crossed);
770
  } elseif (empty($in[$this->key_checkbox_require])) {
771
  add_settings_error($this->option_pw_force_change_name,
772
- $this->option_pw_force_change_name,
773
  $this->hsc_utf8(sprintf($confirm, "Confirm")));
774
  } else {
775
  $result = $this->force_change_for_all();
776
  if ($result === true) {
777
  // Translaton already in WP.
778
  add_settings_error($this->option_pw_force_change_name,
779
- $this->option_pw_force_change_name,
780
  $this->hsc_utf8(__("Success!")), 'updated');
781
  $out = 1;
782
  } else {
783
  add_settings_error($this->option_pw_force_change_name,
784
- $this->option_pw_force_change_name,
785
  $this->hsc_utf8($result));
786
  }
787
  }
391
 
392
  add_settings_section(
393
  self::ID . '-login',
394
+ $this->hsc_utf8(__("Login Failure Policies", self::ID)),
395
  array(&$this, 'section_login'),
396
  self::ID
397
  );
398
  add_settings_section(
399
  self::ID . '-pw',
400
+ $this->hsc_utf8(__("Password Policies", self::ID)),
401
  array(&$this, 'section_blank'),
402
  self::ID
403
  );
404
  add_settings_section(
405
  self::ID . '-misc',
406
+ $this->hsc_utf8(__("Miscellaneous Policies", self::ID)),
407
  array(&$this, 'section_blank'),
408
  self::ID
409
  );
412
  foreach ($this->fields as $id => $field) {
413
  add_settings_field(
414
  $id,
415
+ $this->hsc_utf8($field['label']),
416
  array(&$this, $id),
417
  self::ID,
418
  self::ID . '-' . $field['group']
425
  * @return void
426
  */
427
  public function page_settings() {
428
+ if (is_multisite()) {
429
+ // WordPress doesn't show the successs/error messages on
430
+ // the Network Admin screen, at least in version 3.3.1,
431
+ // so force it to happen for now.
432
+ include_once ABSPATH . 'wp-admin/options-head.php';
433
+ }
434
+
435
  echo '<h2>' . $this->hsc_utf8($this->text_settings) . '</h2>';
436
  echo '<form action="' . $this->hsc_utf8($this->form_action) . '" method="post">' . "\n";
437
  settings_fields($this->option_name);
539
  $out = $this->options_default;
540
  if (!is_array($in)) {
541
  // Not translating this since only hackers will see it.
542
+ add_settings_error($this->option_name,
543
+ $this->hsc_utf8($this->option_name),
544
  'Input must be an array.');
545
  return $out;
546
  }
556
 
557
  if (!is_scalar($in[$name])) {
558
  // Not translating this since only hackers will see it.
559
+ add_settings_error($this->option_name,
560
+ $this->hsc_utf8($name),
561
  $this->hsc_utf8("'" . $field['label'])
562
  . "' was not a scalar, $default");
563
  continue;
567
  case 'bool':
568
  if ($in[$name] != 0 && $in[$name] != 1) {
569
  // Not translating this since only hackers will see it.
570
+ add_settings_error($this->option_name,
571
+ $this->hsc_utf8($name),
572
  $this->hsc_utf8("'" . $field['label']
573
  . "' must be '0' or '1', $default"));
574
  continue 2;
576
  break;
577
  case 'int':
578
  if (!ctype_digit($in[$name])) {
579
+ add_settings_error($this->option_name,
580
+ $this->hsc_utf8($name),
581
  $this->hsc_utf8("'" . $field['label'] . "' "
582
  . __("must be an integer,", self::ID)
583
  . ' ' . $default));
586
  if (array_key_exists('greater_than', $field)
587
  && $in[$name] < $field['greater_than'])
588
  {
589
+ add_settings_error($this->option_name,
590
+ $this->hsc_utf8($name),
591
  $this->hsc_utf8("'" . $field['label'] . "' "
592
  . sprintf($gt_format, $field['greater_than'])
593
  . ' ' . $default));
601
  // Special check to make sure Delay Tier 3 > Delay Tier 2.
602
  $name = 'login_fail_tier_3';
603
  if ($out[$name] <= $out['login_fail_tier_2']) {
604
+ add_settings_error($this->option_name,
605
+ $this->hsc_utf8($name),
606
  $this->hsc_utf8("'" . $this->fields[$name]['label'] . "' "
607
  . sprintf($gt_format, $this->fields['login_fail_tier_2']['label'])
608
  . ' ' . $default));
613
  // Speical check to ensure reuse count is set if aging is enabled.
614
  $name = 'pw_reuse_count';
615
  if ($out['pw_change_days'] && !$out[$name]) {
616
+ add_settings_error($this->option_name,
617
+ $this->hsc_utf8($name),
618
  $this->hsc_utf8("'" . $this->fields[$name]['label'] . "' "
619
  . sprintf($gt_format, 1)
620
  . ' ' . $default));
761
  case $this->text_button_remind:
762
  if (!empty($in[$this->key_checkbox_require])) {
763
  add_settings_error($this->option_pw_force_change_name,
764
+ $this->hsc_utf8($this->option_pw_force_change_name),
765
  $crossed);
766
  } elseif (empty($in[$this->key_checkbox_remind])) {
767
  add_settings_error($this->option_pw_force_change_name,
768
+ $this->hsc_utf8($this->option_pw_force_change_name),
769
  $this->hsc_utf8(sprintf($confirm, "No thanks")));
770
  } else {
771
  // Translaton already in WP.
772
  add_settings_error($this->option_pw_force_change_name,
773
+ $this->hsc_utf8($this->option_pw_force_change_name),
774
  $this->hsc_utf8(__("Success!")),
775
  'updated');
776
  $out = 1;
779
  case $this->text_button_require:
780
  if (!empty($in[$this->key_checkbox_remind])) {
781
  add_settings_error($this->option_pw_force_change_name,
782
+ $this->hsc_utf8($this->option_pw_force_change_name),
783
  $crossed);
784
  } elseif (empty($in[$this->key_checkbox_require])) {
785
  add_settings_error($this->option_pw_force_change_name,
786
+ $this->hsc_utf8($this->option_pw_force_change_name),
787
  $this->hsc_utf8(sprintf($confirm, "Confirm")));
788
  } else {
789
  $result = $this->force_change_for_all();
790
  if ($result === true) {
791
  // Translaton already in WP.
792
  add_settings_error($this->option_pw_force_change_name,
793
+ $this->hsc_utf8($this->option_pw_force_change_name),
794
  $this->hsc_utf8(__("Success!")), 'updated');
795
  $out = 1;
796
  } else {
797
  add_settings_error($this->option_pw_force_change_name,
798
+ $this->hsc_utf8($this->option_pw_force_change_name),
799
  $this->hsc_utf8($result));
800
  }
801
  }
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.4.0
10
  * Author: Daniel Convissor
11
  * Author URI: http://www.analysisandsolutions.com/
12
  * License: GPLv2
@@ -1829,7 +1829,13 @@ class login_security_solution {
1829
  * @uses login_security_solution::$options to hold the data
1830
  */
1831
  protected function set_options() {
1832
- $options = get_option($this->option_name);
 
 
 
 
 
 
1833
  if (!is_array($options)) {
1834
  $options = array();
1835
  }
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.5.0
10
  * Author: Daniel Convissor
11
  * Author URI: http://www.analysisandsolutions.com/
12
  * License: GPLv2
1829
  * @uses login_security_solution::$options to hold the data
1830
  */
1831
  protected function set_options() {
1832
+ if (is_multisite()) {
1833
+ switch_to_blog(1);
1834
+ $options = get_option($this->option_name);
1835
+ restore_current_blog();
1836
+ } else {
1837
+ $options = get_option($this->option_name);
1838
+ }
1839
  if (!is_array($options)) {
1840
  $options = array();
1841
  }
readme.txt CHANGED
@@ -3,13 +3,16 @@ Contributors: convissor
3
  Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=danielc%40analysisandsolutions%2ecom&lc=US&item_name=Donate%3a%20Login%20Security%20Solution&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted
4
  Tags: login, password, idle, timeout, maintenance, security, attack, hack, lock, ban
5
  Requires at least: 3.0
6
- Tested up to: 3.3.1
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
  * Blocks brute force and dictionary attacks without inconveniencing
14
  legitimate users or administrators
15
  + Tracks IP addresses, usernames, and passwords
@@ -185,7 +188,7 @@ A thorough set of unit tests are found in the `tests` directory.
185
  The plugin needs to be installed and activated before running the tests.
186
 
187
  To execute the tests, `cd` into this plugin's directory and
188
- call `phpunit tests` .
189
 
190
  Please note that the tests make extensive use of database transactions.
191
  Many tests will be skipped if your `wp_options` and `wp_usermeta` tables
@@ -211,8 +214,8 @@ are not using the `InnoDB` storage engine.
211
 
212
  A link to the page is found in this plugin's entry in the "Plugins" admin
213
  interface:
214
- * Regular sites: Plugins
215
- * Sites using multisite networks: My Sites | Network Admin | Plugins
216
 
217
  = How do I generate the POT translation file? =
218
 
@@ -225,10 +228,22 @@ then `cd` into that directory and run:
225
 
226
  == Changelog ==
227
 
 
 
 
 
 
 
 
 
 
 
228
  = 0.4.0 =
229
  * Add multisite network support.
230
  * Keep unit tests from deleting settings. Note: removes the ability to
231
  run the unit tests without activating the plugin.
 
 
232
 
233
  = 0.3.0 =
234
  * Use UTF-8 encoding for `htmlspecialchars()` instead of `DB_CHARSET`.
3
  Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=danielc%40analysisandsolutions%2ecom&lc=US&item_name=Donate%3a%20Login%20Security%20Solution&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted
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
18
  + Tracks IP addresses, usernames, and passwords
188
  The plugin needs to be installed and activated before running the tests.
189
 
190
  To execute the tests, `cd` into this plugin's directory and
191
+ call `phpunit tests`
192
 
193
  Please note that the tests make extensive use of database transactions.
194
  Many tests will be skipped if your `wp_options` and `wp_usermeta` tables
214
 
215
  A link to the page is found in this plugin's entry in the "Plugins" admin
216
  interface:
217
+ * Regular sites: Plugins
218
+ * Sites using multisite networks: My Sites | Network Admin | Plugins
219
 
220
  = How do I generate the POT translation file? =
221
 
228
 
229
  == Changelog ==
230
 
231
+ = 0.5.0 =
232
+ * Have multisite network mode use the saved options instead of the defaults.
233
+ * Close more HTML injection vectors. (One would think WordPress' built in
234
+ functions would already do this. Alas...)
235
+ * Get the success/error messages to work when saving settings via the
236
+ Network Admin page.
237
+ * Improve unit tests by ensuring the fail table uses InnoDB.
238
+ * Tested under WordPress 3.3.1 regular and 3.4beta2 multisite.
239
+ * Unit tests pass using PHP 5.4.0RC8-dev, 5.3.11-dev, and 5.2.18-dev.
240
+
241
  = 0.4.0 =
242
  * Add multisite network support.
243
  * Keep unit tests from deleting settings. Note: removes the ability to
244
  run the unit tests without activating the plugin.
245
+ * Tested under WordPress 3.3.1 regular and 3.4beta2 multisite.
246
+ * Unit tests pass using PHP 5.4.0RC8-dev, 5.3.11-dev, and 5.2.18-dev.
247
 
248
  = 0.3.0 =
249
  * Use UTF-8 encoding for `htmlspecialchars()` instead of `DB_CHARSET`.
tests/TestCase.php CHANGED
@@ -258,9 +258,15 @@ abstract class TestCase extends PHPUnit_Framework_TestCase {
258
 
259
  $opt = $wpdb->get_row("SHOW CREATE TABLE `$wpdb->options`", ARRAY_N);
260
  $usr = $wpdb->get_row("SHOW CREATE TABLE `$wpdb->usermeta`", ARRAY_N);
261
-
262
- return (strpos($opt[1], 'ENGINE=InnoDB')
263
- && strpos($usr[1], 'ENGINE=InnoDB'));
 
 
 
 
 
 
264
  }
265
 
266
  /**
258
 
259
  $opt = $wpdb->get_row("SHOW CREATE TABLE `$wpdb->options`", ARRAY_N);
260
  $usr = $wpdb->get_row("SHOW CREATE TABLE `$wpdb->usermeta`", ARRAY_N);
261
+ $fail = $wpdb->get_row("SHOW CREATE TABLE `"
262
+ . self::$lss->table_fail . "`", ARRAY_N);
263
+
264
+ return (
265
+ strpos($opt[1], 'ENGINE=InnoDB')
266
+ && strpos($usr[1], 'ENGINE=InnoDB')
267
+ && !empty($fail)
268
+ && strpos($fail[1], 'ENGINE=InnoDB')
269
+ );
270
  }
271
 
272
  /**