Version Description
- Split user and site info into components before comparing them.
- Increase minimum password length to 10 characters.
Download this release
Release Info
Developer | convissor |
Plugin | Login Security Solution |
Version | 0.23.0 |
Comparing to | |
See all releases |
Code changes from version 0.22.0 to 0.23.0
- admin.php +2 -2
- login-security-solution.php +72 -45
- readme.txt +15 -4
- tests/PasswordValidationTest.php +33 -0
admin.php
CHANGED
@@ -314,9 +314,9 @@ class login_security_solution_admin extends login_security_solution {
|
|
314 |
'pw_length' => array(
|
315 |
'group' => 'pw',
|
316 |
'label' => __("Length", self::ID),
|
317 |
-
'text' => sprintf(__("How long must passwords be? Must be >= %d.", self::ID),
|
318 |
'type' => 'int',
|
319 |
-
'greater_than' =>
|
320 |
),
|
321 |
'pw_complexity_exemption_length' => array(
|
322 |
'group' => 'pw',
|
314 |
'pw_length' => array(
|
315 |
'group' => 'pw',
|
316 |
'label' => __("Length", self::ID),
|
317 |
+
'text' => sprintf(__("How long must passwords be? Must be >= %d.", self::ID), 10),
|
318 |
'type' => 'int',
|
319 |
+
'greater_than' => 10,
|
320 |
),
|
321 |
'pw_complexity_exemption_length' => array(
|
322 |
'group' => 'pw',
|
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.
|
10 |
* Author: Daniel Convissor
|
11 |
* Author URI: http://www.analysisandsolutions.com/
|
12 |
* License: GPLv2
|
@@ -119,7 +119,7 @@ class login_security_solution {
|
|
119 |
'pw_change_days' => 0,
|
120 |
'pw_change_grace_period_minutes' => 15,
|
121 |
'pw_complexity_exemption_length' => 20,
|
122 |
-
'pw_length' =>
|
123 |
'pw_reuse_count' => 0,
|
124 |
);
|
125 |
|
@@ -296,8 +296,8 @@ class login_security_solution {
|
|
296 |
if ($this->options['pw_complexity_exemption_length'] < 20) {
|
297 |
$this->options['pw_complexity_exemption_length'] = 20;
|
298 |
}
|
299 |
-
if ($this->options['pw_length'] <
|
300 |
-
$this->options['pw_length'] =
|
301 |
}
|
302 |
}
|
303 |
|
@@ -1074,12 +1074,21 @@ Password MD5 %5d %s
|
|
1074 |
if (!$string) {
|
1075 |
return false;
|
1076 |
}
|
1077 |
-
|
1078 |
-
|
|
|
|
|
|
|
|
|
1079 |
}
|
1080 |
-
|
1081 |
-
|
|
|
|
|
|
|
|
|
1082 |
}
|
|
|
1083 |
return false;
|
1084 |
}
|
1085 |
|
@@ -1571,46 +1580,18 @@ Password MD5 %5d %s
|
|
1571 |
*/
|
1572 |
protected function is_pw_sequential_file($pw) {
|
1573 |
// First, determine offsets where character type changes occur.
|
1574 |
-
$split =
|
1575 |
-
|
1576 |
-
if (count($split) == 1) {
|
1577 |
-
// All one character type.
|
1578 |
-
$parts_fwd = array($pw);
|
1579 |
-
$parts_rev = array($this->strrev($pw));
|
1580 |
-
} else {
|
1581 |
-
// Multiple character types.
|
1582 |
-
|
1583 |
-
// Don't want info from first element.
|
1584 |
-
array_shift($split);
|
1585 |
|
1586 |
-
|
1587 |
-
|
1588 |
-
$start = 0;
|
1589 |
-
|
1590 |
-
// Now use those offsets to extract the character type blocks.
|
1591 |
-
foreach ($split as $part) {
|
1592 |
-
if ($this->strlen($part[0]) == 1) {
|
1593 |
-
$length = $part[1] - $start;
|
1594 |
-
if ($length > 2) {
|
1595 |
-
// Only examine blocks with 3 or more characters.
|
1596 |
-
$fwd = $this->substr($pw, $start, $length);
|
1597 |
-
$parts_fwd[] = $fwd;
|
1598 |
-
$parts_rev[] = $this->strrev($fwd);
|
1599 |
-
}
|
1600 |
-
$start = $part[1];
|
1601 |
-
}
|
1602 |
-
}
|
1603 |
-
$length = $this->strlen($pw) - $start;
|
1604 |
-
if ($length > 2) {
|
1605 |
-
// Only add the last block if it's 3 or more characters.
|
1606 |
-
$fwd = $this->substr($pw, $start, $length);
|
1607 |
-
$parts_fwd[] = $fwd;
|
1608 |
-
$parts_rev[] = $this->strrev($fwd);
|
1609 |
-
}
|
1610 |
}
|
1611 |
|
1612 |
-
|
1613 |
-
|
|
|
|
|
|
|
|
|
1614 |
}
|
1615 |
|
1616 |
$dir = new DirectoryIterator($this->dir_sequences);
|
@@ -2196,6 +2177,52 @@ Password MD5 %5d %s
|
|
2196 |
return preg_split('/(?<!^)(?!$)/u', $pw);
|
2197 |
}
|
2198 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2199 |
/**
|
2200 |
* Determines how long a string is using mb_strlen() if available
|
2201 |
*
|
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.23.0
|
10 |
* Author: Daniel Convissor
|
11 |
* Author URI: http://www.analysisandsolutions.com/
|
12 |
* License: GPLv2
|
119 |
'pw_change_days' => 0,
|
120 |
'pw_change_grace_period_minutes' => 15,
|
121 |
'pw_complexity_exemption_length' => 20,
|
122 |
+
'pw_length' => 10,
|
123 |
'pw_reuse_count' => 0,
|
124 |
);
|
125 |
|
296 |
if ($this->options['pw_complexity_exemption_length'] < 20) {
|
297 |
$this->options['pw_complexity_exemption_length'] = 20;
|
298 |
}
|
299 |
+
if ($this->options['pw_length'] < 10) {
|
300 |
+
$this->options['pw_length'] = 10;
|
301 |
}
|
302 |
}
|
303 |
|
1074 |
if (!$string) {
|
1075 |
return false;
|
1076 |
}
|
1077 |
+
|
1078 |
+
$split_pw = $this->split_types($pw, 4);
|
1079 |
+
foreach ($split_pw as $element) {
|
1080 |
+
if (stripos($string, $element) !== false) {
|
1081 |
+
return true;
|
1082 |
+
}
|
1083 |
}
|
1084 |
+
|
1085 |
+
$split_string = $this->split_types($string, 4);
|
1086 |
+
foreach ($split_string as $element) {
|
1087 |
+
if (stripos($pw, $element) !== false) {
|
1088 |
+
return true;
|
1089 |
+
}
|
1090 |
}
|
1091 |
+
|
1092 |
return false;
|
1093 |
}
|
1094 |
|
1580 |
*/
|
1581 |
protected function is_pw_sequential_file($pw) {
|
1582 |
// First, determine offsets where character type changes occur.
|
1583 |
+
$split = $this->split_types($pw);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1584 |
|
1585 |
+
if (!$split) {
|
1586 |
+
return false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1587 |
}
|
1588 |
|
1589 |
+
$parts_fwd = array();
|
1590 |
+
$parts_rev = array();
|
1591 |
+
|
1592 |
+
foreach ($split as $part) {
|
1593 |
+
$parts_fwd[] = $part;
|
1594 |
+
$parts_rev[] = $this->strrev($part);
|
1595 |
}
|
1596 |
|
1597 |
$dir = new DirectoryIterator($this->dir_sequences);
|
2177 |
return preg_split('/(?<!^)(?!$)/u', $pw);
|
2178 |
}
|
2179 |
|
2180 |
+
/**
|
2181 |
+
* Breaks string up into blocks of words, numbers, and punctuation
|
2182 |
+
*
|
2183 |
+
* @param string $in the string to process
|
2184 |
+
* @param int $minimum the minimum length string to keep (must be >= 3)
|
2185 |
+
* @return array
|
2186 |
+
*/
|
2187 |
+
protected function split_types($in, $minimum = 3) {
|
2188 |
+
$split = preg_split('/(?<=[^[:punct:]])([[:punct:]])|(?<=[^[:alpha:]])([[:alpha:]])|(?<=\D)(\d)/', $in, -1, PREG_SPLIT_OFFSET_CAPTURE|PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
|
2189 |
+
|
2190 |
+
$out = array();
|
2191 |
+
if (!count($split)) {
|
2192 |
+
// Return empty array, already defined.
|
2193 |
+
} elseif (count($split) == 1) {
|
2194 |
+
// All one character type.
|
2195 |
+
$out[] = trim($in);
|
2196 |
+
} else {
|
2197 |
+
// Multiple character types.
|
2198 |
+
|
2199 |
+
// Ignore meta data about first match.
|
2200 |
+
// Don't worry, the string will be obtained.
|
2201 |
+
array_shift($split);
|
2202 |
+
|
2203 |
+
$start = 0;
|
2204 |
+
|
2205 |
+
// Now use those offsets to extract the character type blocks.
|
2206 |
+
foreach ($split as $part) {
|
2207 |
+
if ($this->strlen($part[0]) == 1) {
|
2208 |
+
$length = $part[1] - $start;
|
2209 |
+
$tmp = trim($this->substr($in, $start, $length));
|
2210 |
+
if ($this->strlen($tmp) >= $minimum) {
|
2211 |
+
$out[] = $tmp;
|
2212 |
+
}
|
2213 |
+
$start = $part[1];
|
2214 |
+
}
|
2215 |
+
}
|
2216 |
+
$length = $this->strlen($in) - $start;
|
2217 |
+
$tmp = trim($this->substr($in, $start, $length));
|
2218 |
+
if ($this->strlen($tmp) >= $minimum) {
|
2219 |
+
$out[] = $tmp;
|
2220 |
+
}
|
2221 |
+
}
|
2222 |
+
|
2223 |
+
return $out;
|
2224 |
+
}
|
2225 |
+
|
2226 |
/**
|
2227 |
* Determines how long a string is using mb_strlen() if available
|
2228 |
*
|
readme.txt
CHANGED
@@ -4,7 +4,7 @@ Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=danie
|
|
4 |
Tags: login, password, passwords, strength, strong, strong passwords, password strength, idle, timeout, maintenance, security, attack, hack, lock, lockdown, ban, brute force, brute, force, authentication, auth, cookie, users
|
5 |
Requires at least: 3.3
|
6 |
Tested up to: 3.4.1
|
7 |
-
Stable tag: 0.
|
8 |
|
9 |
Security against brute force attacks by tracking IP, name, password; requiring very strong passwords. Idle timeout. Maintenance mode lockdown.
|
10 |
|
@@ -37,9 +37,8 @@ The tests have caught every password dictionary entry I've tried.
|
|
37 |
+ Minimum length (customizable)
|
38 |
+ Doesn't match blog info
|
39 |
+ Doesn't match user data
|
40 |
-
+ Must either have numbers
|
41 |
-
|
42 |
-
very long
|
43 |
+ Non-sequential codepoints
|
44 |
+ Non-sequential keystrokes (custom sequence files can be added)
|
45 |
+ Not in the password dictionary files you've provided (if any)
|
@@ -88,6 +87,13 @@ The tests have caught every password dictionary entry I've tried.
|
|
88 |
* Unit tests covering 100% of the main class
|
89 |
* Internationalized unit tests
|
90 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
91 |
|
92 |
= Translations =
|
93 |
|
@@ -311,6 +317,10 @@ implementation, use the script I made for generating all of the .mo files:
|
|
311 |
|
312 |
== Changelog ==
|
313 |
|
|
|
|
|
|
|
|
|
314 |
= 0.22.0 =
|
315 |
* Track a given IP, user name, password combination only once.
|
316 |
* Prevent "not a valid MySQL-Link resource" on auth cookie failure.
|
@@ -489,6 +499,7 @@ You can also [view our existing tickets](https://plugins.trac.wordpress.org/quer
|
|
489 |
+ [Consumer Password Worst Practices](http://www.imperva.com/docs/WP_Consumer_Password_Worst_Practices.pdf), Imperva
|
490 |
+ [Preventing Brute Force Attacks on your Web Login](http://www.bryanrite.com/preventing-brute-force-attacks-on-your-web-login/), Bryan Rite
|
491 |
+ [Password Strength](http://xkcd.com/936/), Randall Munroe
|
|
|
492 |
|
493 |
* Technical Info
|
494 |
+ [The Extreme UTF-8 Table](http://doc.infosnel.nl/extreme_utf-8.html), infosnel.nl
|
4 |
Tags: login, password, passwords, strength, strong, strong passwords, password strength, idle, timeout, maintenance, security, attack, hack, lock, lockdown, ban, brute force, brute, force, authentication, auth, cookie, users
|
5 |
Requires at least: 3.3
|
6 |
Tested up to: 3.4.1
|
7 |
+
Stable tag: 0.23.0
|
8 |
|
9 |
Security against brute force attacks by tracking IP, name, password; requiring very strong passwords. Idle timeout. Maintenance mode lockdown.
|
10 |
|
37 |
+ Minimum length (customizable)
|
38 |
+ Doesn't match blog info
|
39 |
+ Doesn't match user data
|
40 |
+
+ Must either have numbers, punctuation, upper and lower case characters
|
41 |
+
or be very long
|
|
|
42 |
+ Non-sequential codepoints
|
43 |
+ Non-sequential keystrokes (custom sequence files can be added)
|
44 |
+ Not in the password dictionary files you've provided (if any)
|
87 |
* Unit tests covering 100% of the main class
|
88 |
* Internationalized unit tests
|
89 |
|
90 |
+
For reference, the similar plugins include:
|
91 |
+
|
92 |
+
* [Better WP Security](http://wordpress.org/extend/plugins/better-wp-security/)
|
93 |
+
* [Login Lock](http://wordpress.org/extend/plugins/login-lock/)
|
94 |
+
* [PMC Lockdown](http://wordpress.org/extend/plugins/pmc-lockdown/)
|
95 |
+
* [Simple Login Lockdown](http://wordpress.org/extend/plugins/simple-login-lockdown/)
|
96 |
+
|
97 |
|
98 |
= Translations =
|
99 |
|
317 |
|
318 |
== Changelog ==
|
319 |
|
320 |
+
= 0.23.0 =
|
321 |
+
* Split user and site info into components before comparing them.
|
322 |
+
* Increase minimum password length to 10 characters.
|
323 |
+
|
324 |
= 0.22.0 =
|
325 |
* Track a given IP, user name, password combination only once.
|
326 |
* Prevent "not a valid MySQL-Link resource" on auth cookie failure.
|
499 |
+ [Consumer Password Worst Practices](http://www.imperva.com/docs/WP_Consumer_Password_Worst_Practices.pdf), Imperva
|
500 |
+ [Preventing Brute Force Attacks on your Web Login](http://www.bryanrite.com/preventing-brute-force-attacks-on-your-web-login/), Bryan Rite
|
501 |
+ [Password Strength](http://xkcd.com/936/), Randall Munroe
|
502 |
+
+ [Why passwords have never been weaker -- and crackers have never been stronger](http://arstechnica.com/security/2012/08/passwords-under-assault/), Dan Goodin
|
503 |
|
504 |
* Technical Info
|
505 |
+ [The Extreme UTF-8 Table](http://doc.infosnel.nl/extreme_utf-8.html), infosnel.nl
|
tests/PasswordValidationTest.php
CHANGED
@@ -427,6 +427,39 @@ class PasswordValidationTest extends TestCase {
|
|
427 |
}
|
428 |
}
|
429 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
430 |
public function test_strip_nonword_chars() {
|
431 |
$tests = array(
|
432 |
"^a&*b()c2" => "abc2",
|
427 |
}
|
428 |
}
|
429 |
|
430 |
+
public function test_split_types_default3() {
|
431 |
+
$tests = array(
|
432 |
+
"^&*()" => array("^&*()"),
|
433 |
+
"ad" => array("ad"),
|
434 |
+
"asd" => array("asd"),
|
435 |
+
"1234" => array("1234"),
|
436 |
+
"adgi!15yYui5889" => array("adgi", "yYui", "5889"),
|
437 |
+
"adgi!1365yYui5" => array("adgi", "1365", "yYui"),
|
438 |
+
"adgi!1365yYui59" => array("adgi", "1365", "yYui"),
|
439 |
+
"adgi 1365 yYui" => array("adgi", "1365", "yYui"),
|
440 |
+
"adgi song yYui" => array("adgi", "song", "yYui"),
|
441 |
+
);
|
442 |
+
foreach ($tests as $pw => $expected) {
|
443 |
+
$actual = self::$lss->split_types($pw);
|
444 |
+
$this->assertEquals($expected, $actual);
|
445 |
+
}
|
446 |
+
}
|
447 |
+
|
448 |
+
public function test_split_types_5() {
|
449 |
+
$tests = array(
|
450 |
+
"^&*()" => array("^&*()"),
|
451 |
+
"ad" => array("ad"),
|
452 |
+
"asd" => array("asd"),
|
453 |
+
"1234" => array("1234"),
|
454 |
+
"adgiii!.^#--?133655yaaYui" => array("adgiii", "!.^#--?", "133655", "yaaYui"),
|
455 |
+
"adgi!13355yYui59" => array("13355"),
|
456 |
+
);
|
457 |
+
foreach ($tests as $pw => $expected) {
|
458 |
+
$actual = self::$lss->split_types($pw, 5);
|
459 |
+
$this->assertEquals($expected, $actual);
|
460 |
+
}
|
461 |
+
}
|
462 |
+
|
463 |
public function test_strip_nonword_chars() {
|
464 |
$tests = array(
|
465 |
"^a&*b()c2" => "abc2",
|