From 398c9a3102650a2a9edc97d270a7efa314b7e227 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 15 May 2019 17:40:11 +0200 Subject: [PATCH] Override policy rules if password shorter than a certain value One may define weak policy rules for long passwords (encouraging passphrases) and override these weak rules by harder ones if the password is shorter. --- conf/config.inc.php | 15 +++++++++++++++ index.php | 4 +++- lang/ca.inc.php | 1 + lang/cn.inc.php | 1 + lang/cs.inc.php | 1 + lang/de.inc.php | 1 + lang/ee.inc.php | 1 + lang/el.inc.php | 1 + lang/en.inc.php | 1 + lang/es.inc.php | 1 + lang/fr.inc.php | 1 + lang/hu.inc.php | 1 + lang/it.inc.php | 1 + lang/ja.inc.php | 1 + lang/nb-NO.inc.php | 1 + lang/nl.inc.php | 1 + lang/pl.inc.php | 1 + lang/pt-BR.inc.php | 1 + lang/pt-PT.inc.php | 1 + lang/ru.inc.php | 1 + lang/sk.inc.php | 1 + lang/sl.inc.php | 1 + lang/sv.inc.php | 1 + lang/tr.inc.php | 1 + lang/uk.inc.php | 1 + lang/zh-CN.inc.php | 1 + lang/zh-TW.inc.php | 1 + lib/functions.inc.php | 35 ++++++++++++++++++++++++++++++++++- tests/CheckPasswordTest.php | 29 +++++++++++++++++++++++++++++ 29 files changed, 106 insertions(+), 2 deletions(-) diff --git a/conf/config.inc.php b/conf/config.inc.php index f1f6853e5..62d22e086 100644 --- a/conf/config.inc.php +++ b/conf/config.inc.php @@ -111,6 +111,21 @@ $pwd_complexity = 0; # use pwnedpasswords api v2 to securely check if the password has been on a leak $use_pwnedpasswords = false; +# override policy if password is shorter than a certain value +# Example: +# $pwd_min_length = 12; +# $pwd_override_by_length = array( +# array( +# 'valid_if_shorter_than' => 12, +# 'rules' => array( +# 'pwd_min_length' => 8, +# 'pwd_min_lower' => 1, +# 'pwd_min_upper' => 1, +# 'pwd_min_digit' => 1, +# 'pwd_min_special' => 1 +# ) +# ) +#); # Show policy constraints message: # always # never diff --git a/index.php b/index.php index 10a4a40d4..6cc3f63a3 100644 --- a/index.php +++ b/index.php @@ -122,6 +122,7 @@ if (!isset($ldap_login_attribute)) { $ldap_login_attribute = "uid"; } if (!isset($ldap_fullname_attribute)) { $ldap_fullname_attribute = "cn"; } if (!isset($pwd_forbidden_chars)) { $pwd_forbidden_chars = ""; } +if (!isset($pwd_override_by_length)) { $pwd_override_by_length = array(); } if (!isset($hash_options)) { $hash_options = array(); } if (!isset($samba_options)) { $samba_options = array(); } if (!isset($ldap_starttls)) { $ldap_starttls = false; } @@ -141,7 +142,8 @@ "pwd_diff_login" => $pwd_diff_login, "pwd_complexity" => $pwd_complexity, "use_pwnedpasswords" => $use_pwnedpasswords, - "pwd_no_special_at_ends" => $pwd_no_special_at_ends + "pwd_no_special_at_ends" => $pwd_no_special_at_ends, + "pwd_override_by_length" => $pwd_override_by_length, ); if (!isset($pwd_show_policy_pos)) { $pwd_show_policy_pos = "above"; } diff --git a/lang/ca.inc.php b/lang/ca.inc.php index 95cdcf034..e0ddf4c9c 100644 --- a/lang/ca.inc.php +++ b/lang/ca.inc.php @@ -137,3 +137,4 @@ $messages['policypwned'] = "Your new password may not be published on any previous public password leak from any site"; $messages['specialatends'] = "Your new password has its only special character at the beginning or end"; $messages['policyspecialatends'] = "Your new password may not have its only special character at the beginning or end"; +$messages['overriding_policy_by_length'] = "If the password is shorter than %d characters, the following rules apply:"; diff --git a/lang/cn.inc.php b/lang/cn.inc.php index a20352999..9df875c37 100644 --- a/lang/cn.inc.php +++ b/lang/cn.inc.php @@ -133,3 +133,4 @@ $messages['policypwned'] = "Your new password may not be published on any previous public password leak from any site"; $messages['specialatends'] = "Your new password has its only special character at the beginning or end"; $messages['policyspecialatends'] = "Your new password may not have its only special character at the beginning or end"; +$messages['overriding_policy_by_length'] = "If the password is shorter than %d characters, the following rules apply:"; diff --git a/lang/cs.inc.php b/lang/cs.inc.php index 9fc429238..ce3f3e49d 100644 --- a/lang/cs.inc.php +++ b/lang/cs.inc.php @@ -133,3 +133,4 @@ $messages['policypwned'] = "Your new password may not be published on any previous public password leak from any site"; $messages['specialatends'] = "Your new password has its only special character at the beginning or end"; $messages['policyspecialatends'] = "Your new password may not have its only special character at the beginning or end"; +$messages['overriding_policy_by_length'] = "If the password is shorter than %d characters, the following rules apply:"; diff --git a/lang/de.inc.php b/lang/de.inc.php index 371d6363c..adc376f5c 100644 --- a/lang/de.inc.php +++ b/lang/de.inc.php @@ -135,3 +135,4 @@ $messages['policypwned'] = "Your new password may not be published on any previous public password leak from any site"; $messages['specialatends'] = "Ihr Passwort enthält das einzige Sonderzeichen am Anfang oder am Ende"; $messages['policyspecialatends'] = "Ein Sonderzeichen, wenn es nur ein einziges gibt, darf nicht am Anfang oder am Ende stehen"; +$messages['overriding_policy_by_length'] = "Wenn das Passwort kürzer als %d Zeichen ist, gelten folgende Regeln:"; diff --git a/lang/ee.inc.php b/lang/ee.inc.php index dbeb1bf57..061992b99 100644 --- a/lang/ee.inc.php +++ b/lang/ee.inc.php @@ -135,3 +135,4 @@ $messages['policypwned'] = "Your new password may not be published on any previous public password leak from any site"; $messages['specialatends'] = "Your new password has its only special character at the beginning or end"; $messages['policyspecialatends'] = "Your new password may not have its only special character at the beginning or end"; +$messages['overriding_policy_by_length'] = "If the password is shorter than %d characters, the following rules apply:"; diff --git a/lang/el.inc.php b/lang/el.inc.php index 82dd8cfd7..996f23f40 100644 --- a/lang/el.inc.php +++ b/lang/el.inc.php @@ -133,3 +133,4 @@ $messages['policypwned'] = "Your new password may not be published on any previous public password leak from any site"; $messages['specialatends'] = "Your new password has its only special character at the beginning or end"; $messages['policyspecialatends'] = "Your new password may not have its only special character at the beginning or end"; +$messages['overriding_policy_by_length'] = "If the password is shorter than %d characters, the following rules apply:"; diff --git a/lang/en.inc.php b/lang/en.inc.php index 295500d4a..fecfd69d8 100644 --- a/lang/en.inc.php +++ b/lang/en.inc.php @@ -133,3 +133,4 @@ $messages['policypwned'] = "Your new password may not be published on any previous public password leak from any site"; $messages['specialatends'] = "Your new password has its only special character at the beginning or end"; $messages['policyspecialatends'] = "Your new password may not have its only special character at the beginning or end"; +$messages['overriding_policy_by_length'] = "If the password is shorter than %d characters, the following rules apply:"; diff --git a/lang/es.inc.php b/lang/es.inc.php index 4ac2433c7..989c1af18 100644 --- a/lang/es.inc.php +++ b/lang/es.inc.php @@ -134,3 +134,4 @@ $messages['policypwned'] = "Su contraseña no puede haber sido publicada previamente en ninguna lista de contraseñas filtradas accesible al publico de ningun sitio"; $messages['specialatends'] = "Your new password has its only special character at the beginning or end"; $messages['policyspecialatends'] = "Your new password may not have its only special character at the beginning or end"; +$messages['overriding_policy_by_length'] = "If the password is shorter than %d characters, the following rules apply:"; diff --git a/lang/fr.inc.php b/lang/fr.inc.php index 6ff7e4ad2..196c91a07 100644 --- a/lang/fr.inc.php +++ b/lang/fr.inc.php @@ -133,3 +133,4 @@ $messages['policypwned'] = "Your new password may not be published on any previous public password leak from any site"; $messages['specialatends'] = "Your new password has its only special character at the beginning or end"; $messages['policyspecialatends'] = "Your new password may not have its only special character at the beginning or end"; +$messages['overriding_policy_by_length'] = "If the password is shorter than %d characters, the following rules apply:"; diff --git a/lang/hu.inc.php b/lang/hu.inc.php index c620b4f6b..f6d4d73a8 100644 --- a/lang/hu.inc.php +++ b/lang/hu.inc.php @@ -133,3 +133,4 @@ $messages['policypwned'] = "Your new password may not be published on any previous public password leak from any site"; $messages['specialatends'] = "Your new password has its only special character at the beginning or end"; $messages['policyspecialatends'] = "Your new password may not have its only special character at the beginning or end"; +$messages['overriding_policy_by_length'] = "If the password is shorter than %d characters, the following rules apply:"; diff --git a/lang/it.inc.php b/lang/it.inc.php index 3b8e5b316..a7daea9f0 100644 --- a/lang/it.inc.php +++ b/lang/it.inc.php @@ -133,3 +133,4 @@ $messages['policypwned'] = "Your new password may not be published on any previous public password leak from any site"; $messages['specialatends'] = "Your new password has its only special character at the beginning or end"; $messages['policyspecialatends'] = "Your new password may not have its only special character at the beginning or end"; +$messages['overriding_policy_by_length'] = "If the password is shorter than %d characters, the following rules apply:"; diff --git a/lang/ja.inc.php b/lang/ja.inc.php index 223eb4924..b1159fc6f 100644 --- a/lang/ja.inc.php +++ b/lang/ja.inc.php @@ -133,3 +133,4 @@ $messages['policypwned'] = "Your new password may not be published on any previous public password leak from any site"; $messages['specialatends'] = "Your new password has its only special character at the beginning or end"; $messages['policyspecialatends'] = "Your new password may not have its only special character at the beginning or end"; +$messages['overriding_policy_by_length'] = "If the password is shorter than %d characters, the following rules apply:"; diff --git a/lang/nb-NO.inc.php b/lang/nb-NO.inc.php index 2bf468225..74a53a139 100644 --- a/lang/nb-NO.inc.php +++ b/lang/nb-NO.inc.php @@ -133,3 +133,4 @@ $messages['policypwned'] = "Ditt nye passord er ikke publisert på kjente passord-leak siter"; $messages['specialatends'] = "Your new password has its only special character at the beginning or end"; $messages['policyspecialatends'] = "Your new password may not have its only special character at the beginning or end"; +$messages['overriding_policy_by_length'] = "If the password is shorter than %d characters, the following rules apply:"; diff --git a/lang/nl.inc.php b/lang/nl.inc.php index 11ce31193..286d38640 100644 --- a/lang/nl.inc.php +++ b/lang/nl.inc.php @@ -135,3 +135,4 @@ $messages['policypwned'] = "Your new password may not be published on any previous public password leak from any site"; $messages['specialatends'] = "Your new password has its only special character at the beginning or end"; $messages['policyspecialatends'] = "Your new password may not have its only special character at the beginning or end"; +$messages['overriding_policy_by_length'] = "If the password is shorter than %d characters, the following rules apply:"; diff --git a/lang/pl.inc.php b/lang/pl.inc.php index 5b805d609..81712d377 100644 --- a/lang/pl.inc.php +++ b/lang/pl.inc.php @@ -135,3 +135,4 @@ $messages['policypwned'] = "Your new password may not be published on any previous public password leak from any site"; $messages['specialatends'] = "Your new password has its only special character at the beginning or end"; $messages['policyspecialatends'] = "Your new password may not have its only special character at the beginning or end"; +$messages['overriding_policy_by_length'] = "If the password is shorter than %d characters, the following rules apply:"; diff --git a/lang/pt-BR.inc.php b/lang/pt-BR.inc.php index f600de2b5..f2b66c150 100644 --- a/lang/pt-BR.inc.php +++ b/lang/pt-BR.inc.php @@ -133,3 +133,4 @@ $messages['policypwned'] = "Parece que sua nova senha não foi publicada como vazada de qualquer site"; $messages['specialatends'] = "Your new password has its only special character at the beginning or end"; $messages['policyspecialatends'] = "Your new password may not have its only special character at the beginning or end"; +$messages['overriding_policy_by_length'] = "If the password is shorter than %d characters, the following rules apply:"; diff --git a/lang/pt-PT.inc.php b/lang/pt-PT.inc.php index 775ba6e02..4ca6171ba 100644 --- a/lang/pt-PT.inc.php +++ b/lang/pt-PT.inc.php @@ -133,3 +133,4 @@ $messages['policypwned'] = "Your new password may not be published on any previous public password leak from any site"; $messages['specialatends'] = "Your new password has its only special character at the beginning or end"; $messages['policyspecialatends'] = "Your new password may not have its only special character at the beginning or end"; +$messages['overriding_policy_by_length'] = "If the password is shorter than %d characters, the following rules apply:"; diff --git a/lang/ru.inc.php b/lang/ru.inc.php index cb5f744a3..2dfdf34ce 100644 --- a/lang/ru.inc.php +++ b/lang/ru.inc.php @@ -133,3 +133,4 @@ $messages['policypwned'] = "Your new password may not be published on any previous public password leak from any site"; $messages['specialatends'] = "Your new password has its only special character at the beginning or end"; $messages['policyspecialatends'] = "Your new password may not have its only special character at the beginning or end"; +$messages['overriding_policy_by_length'] = "If the password is shorter than %d characters, the following rules apply:"; diff --git a/lang/sk.inc.php b/lang/sk.inc.php index f595d0e7e..64df4d398 100644 --- a/lang/sk.inc.php +++ b/lang/sk.inc.php @@ -133,3 +133,4 @@ $messages['policypwned'] = "Your new password may not be published on any previous public password leak from any site"; $messages['specialatends'] = "Your new password has its only special character at the beginning or end"; $messages['policyspecialatends'] = "Your new password may not have its only special character at the beginning or end"; +$messages['overriding_policy_by_length'] = "If the password is shorter than %d characters, the following rules apply:"; diff --git a/lang/sl.inc.php b/lang/sl.inc.php index 28465340a..2645ac9d0 100644 --- a/lang/sl.inc.php +++ b/lang/sl.inc.php @@ -133,3 +133,4 @@ $messages['policypwned'] = "Your new password may not be published on any previous public password leak from any site"; $messages['specialatends'] = "Your new password has its only special character at the beginning or end"; $messages['policyspecialatends'] = "Your new password may not have its only special character at the beginning or end"; +$messages['overriding_policy_by_length'] = "If the password is shorter than %d characters, the following rules apply:"; diff --git a/lang/sv.inc.php b/lang/sv.inc.php index fb143b2f8..d0c8371c7 100644 --- a/lang/sv.inc.php +++ b/lang/sv.inc.php @@ -133,3 +133,4 @@ $messages['policypwned'] = "Your new password may not be published on any previous public password leak from any site"; $messages['specialatends'] = "Your new password has its only special character at the beginning or end"; $messages['policyspecialatends'] = "Your new password may not have its only special character at the beginning or end"; +$messages['overriding_policy_by_length'] = "If the password is shorter than %d characters, the following rules apply:"; diff --git a/lang/tr.inc.php b/lang/tr.inc.php index ee24bffca..8cf673923 100644 --- a/lang/tr.inc.php +++ b/lang/tr.inc.php @@ -133,3 +133,4 @@ $messages['policypwned'] = "Your new password may not be published on any previous public password leak from any site"; $messages['specialatends'] = "Your new password has its only special character at the beginning or end"; $messages['policyspecialatends'] = "Your new password may not have its only special character at the beginning or end"; +$messages['overriding_policy_by_length'] = "If the password is shorter than %d characters, the following rules apply:"; diff --git a/lang/uk.inc.php b/lang/uk.inc.php index b5f5f3eb7..2b5ec1546 100644 --- a/lang/uk.inc.php +++ b/lang/uk.inc.php @@ -134,3 +134,4 @@ $messages['policypwned'] = "Your new password may not be published on any previous public password leak from any site"; $messages['specialatends'] = "Your new password has its only special character at the beginning or end"; $messages['policyspecialatends'] = "Your new password may not have its only special character at the beginning or end"; +$messages['overriding_policy_by_length'] = "If the password is shorter than %d characters, the following rules apply:"; diff --git a/lang/zh-CN.inc.php b/lang/zh-CN.inc.php index b906ffa3b..d3d38edb4 100644 --- a/lang/zh-CN.inc.php +++ b/lang/zh-CN.inc.php @@ -133,3 +133,4 @@ $messages['policypwned'] = "Your new password may not be published on any previous public password leak from any site"; $messages['specialatends'] = "Your new password has its only special character at the beginning or end"; $messages['policyspecialatends'] = "Your new password may not have its only special character at the beginning or end"; +$messages['overriding_policy_by_length'] = "If the password is shorter than %d characters, the following rules apply:"; diff --git a/lang/zh-TW.inc.php b/lang/zh-TW.inc.php index 090ef305a..c62987e63 100644 --- a/lang/zh-TW.inc.php +++ b/lang/zh-TW.inc.php @@ -133,3 +133,4 @@ $messages['policypwned'] = "Your new password may not be published on any previous public password leak from any site"; $messages['specialatends'] = "Your new password has its only special character at the beginning or end"; $messages['policyspecialatends'] = "Your new password may not have its only special character at the beginning or end"; +$messages['overriding_policy_by_length'] = "If the password is shorter than %d characters, the following rules apply:"; diff --git a/lib/functions.inc.php b/lib/functions.inc.php index a3eb0bbfc..08438d9b3 100644 --- a/lib/functions.inc.php +++ b/lib/functions.inc.php @@ -180,6 +180,17 @@ function show_policy( $messages, $pwd_policy_config, $result ) { # Display bloc echo "
\n"; echo "

".$messages["policy"]."

\n"; + output_policy_list($messages, $pwd_policy_config); + usort($pwd_override_by_length, function($a, $b) { return $b['valid_if_shorter_than'] - $a['valid_if_shorter_than']; }); + foreach($pwd_override_by_length as $override) { + echo "

".sprintf($messages["overriding_policy_by_length"], $override['valid_if_shorter_than']) . "

\n"; + output_policy_list($messages, $override['rules']); + } + echo "
\n"; +} + +function output_policy_list($messages, $policy) { + extract($policy); echo "\n"; - echo "\n"; } # Check password strength @@ -205,6 +215,29 @@ function check_password_strength( $password, $oldpassword, $pwd_policy_config, $ $result = ""; $length = strlen(utf8_decode($password)); + + usort($pwd_override_by_length, function($a, $b) { return $a['valid_if_shorter_than'] - $b['valid_if_shorter_than']; }); + $checked = false; + foreach($pwd_override_by_length as $override) { + if($length < $override['valid_if_shorter_than']) { + $policy = array_merge($pwd_policy_config, $override['rules']); + $result = match_policy($password, $oldpassword, $policy, $login, $length); + $checked = true; + break; + } + } + if(!$checked) { + $result = match_policy($password, $oldpassword, $pwd_policy_config, $login, $length); + } + + return $result; +} + +function match_policy($password, $oldpassword, $pwd_policy_config, $login, $length) { + extract( $pwd_policy_config ); + + $result = ""; + preg_match_all("/[a-z]/", $password, $lower_res); $lower = count( $lower_res[0] ); preg_match_all("/[A-Z]/", $password, $upper_res); diff --git a/tests/CheckPasswordTest.php b/tests/CheckPasswordTest.php index 022f93158..d4e32f921 100644 --- a/tests/CheckPasswordTest.php +++ b/tests/CheckPasswordTest.php @@ -29,6 +29,7 @@ public function testCheckPasswordStrength() "pwd_complexity" => 0, "use_pwnedpasswords" => false, "pwd_no_special_at_ends" => false, + "pwd_override_by_length" => array(), ); $login = "coudot"; @@ -59,11 +60,38 @@ public function testCheckPasswordStrength() "pwd_complexity" => 3, "use_pwnedpasswords" => false, "pwd_no_special_at_ends" => true, + "pwd_override_by_length" => array( + array( + 'valid_if_shorter_than' => 5, + 'rules' => array( + 'pwd_min_length' => 4, + 'pwd_min_digit' => 1, + 'pwd_min_upper' => 1, + 'pwd_min_lower' => 1, + 'pwd_min_special' => 1, + 'pwd_complexity' => 0 + ) + ), + array( + 'valid_if_shorter_than' => 4, + 'rules' => array( + 'pwd_min_length' => 3, + 'pwd_min_lower' => 1, + 'pwd_min_special' => 2, + 'pwd_complexity' => 0 + ) + ) + ), ); $this->assertEquals("notcomplex", check_password_strength( "simple", $oldpassword, $pwd_policy_config, $login ) ); $this->assertEquals("specialatends", check_password_strength( "!simple", $oldpassword, $pwd_policy_config, $login ) ); $this->assertEquals("specialatends", check_password_strength( "simple?", $oldpassword, $pwd_policy_config, $login ) ); + $this->assertEquals("minspecial", check_password_strength( "xyZ1", $oldpassword, $pwd_policy_config, $login ) ); + $this->assertEquals("minupper", check_password_strength( "xy%1", $oldpassword, $pwd_policy_config, $login ) ); + $this->assertEquals("", check_password_strength( "xY%1", $oldpassword, $pwd_policy_config, $login ) ); + $this->assertEquals("minspecial", check_password_strength( "x%1", $oldpassword, $pwd_policy_config, $login ) ); + $this->assertEquals("", check_password_strength( "x%$", $oldpassword, $pwd_policy_config, $login ) ); $this->assertEquals("", check_password_strength( "C0mplex", $oldpassword, $pwd_policy_config, $login ) ); $this->assertEquals("", check_password_strength( "C0!mplex", $oldpassword, $pwd_policy_config, $login ) ); $this->assertEquals("", check_password_strength( "%C0!mplex", $oldpassword, $pwd_policy_config, $login ) ); @@ -99,6 +127,7 @@ public function testCheckPasswordStrengthPwnedPasswords() "pwd_complexity" => 0, "use_pwnedpasswords" => true, "pwd_no_special_at_ends" => false, + "pwd_override_by_length" => array(), ); $this->assertEquals("pwned", check_password_strength( "!1Password", $oldpassword, $pwd_policy_config, $login ) );