extended password policy
This commit is contained in:
parent
85f21de260
commit
b1097df77b
Binary file not shown.
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 28 KiB |
|
@ -159,6 +159,12 @@ $helpArray = array (
|
|||
"Text" => _('Defines if the PHP error reporting setting from php.ini is used or the setting preferred by LAM ("E_ALL & ~E_NOTICE"). If you do not develop LAM modules please use the default. This will prevent displaying messages that are useful only for developers.')),
|
||||
"245" => array ("Headline" => _('Encrypt session'),
|
||||
"Text" => _('Encrypts sensitive data like passwords in your session. This requires the PHP MCrypt extension.')),
|
||||
"246" => array ("Headline" => _('Number of rules that must match'),
|
||||
"Text" => _('Specifies the number of above password rules that must be fulfilled.')),
|
||||
"247" => array ("Headline" => _('Password must not contain user name'),
|
||||
"Text" => _('Specifies if the password must not contain the user name.')),
|
||||
"248" => array ("Headline" => _('Password must not contain part of user/first/last name'),
|
||||
"Text" => _('Specifies if the password must not contain 3 or more characters of the user/first/last name.')),
|
||||
"250" => array ("Headline" => _("Filter"),
|
||||
"Text" => _("Here you can input simple filter expressions (e.g. 'value' or 'v*'). The filter is case-sensitive.")),
|
||||
"260" => array ("Headline" => _("Additional LDAP filter"),
|
||||
|
|
|
@ -1506,6 +1506,15 @@ class LAMCfgMain {
|
|||
/** minimum character classes (upper, lower, numeric, symbols) */
|
||||
public $passwordMinClasses = 0;
|
||||
|
||||
/** number of password rules that must match (-1 = all) */
|
||||
public $checkedRulesCount = -1;
|
||||
|
||||
/** password may contain the user name */
|
||||
public $passwordMustNotContainUser = 'false';
|
||||
|
||||
/** password may contain more than 2 characters of user/first/last name */
|
||||
public $passwordMustNotContain3Chars = 'false';
|
||||
|
||||
/** path to config file */
|
||||
private $conffile;
|
||||
|
||||
|
@ -1525,8 +1534,9 @@ class LAMCfgMain {
|
|||
private $settings = array("password", "default", "sessionTimeout",
|
||||
"logLevel", "logDestination", "allowedHosts", "passwordMinLength",
|
||||
"passwordMinUpper", "passwordMinLower", "passwordMinNumeric",
|
||||
"passwordMinClasses", "passwordMinSymbol", "mailEOL", 'errorReporting',
|
||||
'encryptSession', 'allowedHostsSelfService');
|
||||
"passwordMinClasses", "passwordMinSymbol", 'checkedRulesCount',
|
||||
'passwordMustNotContainUser', 'passwordMustNotContain3Chars',
|
||||
"mailEOL", 'errorReporting', 'encryptSession', 'allowedHostsSelfService');
|
||||
|
||||
/**
|
||||
* Loads preferences from config file
|
||||
|
@ -1615,6 +1625,9 @@ class LAMCfgMain {
|
|||
if (!in_array("passwordMinNumeric", $saved)) array_push($file_array, "\n\n# Password: minimum numeric characters\n" . "passwordMinNumeric: " . $this->passwordMinNumeric);
|
||||
if (!in_array("passwordMinSymbol", $saved)) array_push($file_array, "\n\n# Password: minimum symbolic characters\n" . "passwordMinSymbol: " . $this->passwordMinSymbol);
|
||||
if (!in_array("passwordMinClasses", $saved)) array_push($file_array, "\n\n# Password: minimum character classes (0-4)\n" . "passwordMinClasses: " . $this->passwordMinClasses);
|
||||
if (!in_array("checkedRulesCount", $saved)) array_push($file_array, "\n\n# Password: checked rules\n" . "checkedRulesCount: " . $this->checkedRulesCount);
|
||||
if (!in_array("passwordMustNotContain3Chars", $saved)) array_push($file_array, "\n\n# Password: must not contain part of user name\n" . "passwordMustNotContain3Chars: " . $this->passwordMustNotContain3Chars);
|
||||
if (!in_array("passwordMustNotContainUser", $saved)) array_push($file_array, "\n\n# Password: must not contain user name\n" . "passwordMustNotContainUser: " . $this->passwordMustNotContainUser);
|
||||
if (!in_array("mailEOL", $saved)) array_push($file_array, "\n\n# Email format (default/unix)\n" . "mailEOL: " . $this->mailEOL);
|
||||
if (!in_array("errorReporting", $saved)) array_push($file_array, "\n\n# PHP error reporting (default/system)\n" . "errorReporting: " . $this->errorReporting);
|
||||
$file = @fopen($this->conffile, "w");
|
||||
|
|
|
@ -315,10 +315,12 @@ function checkIfDeleteEntriesIsAllowed($scope) {
|
|||
/**
|
||||
* Checks if the password fulfills the password policies.
|
||||
*
|
||||
* @param string $password password
|
||||
* @param String $password password
|
||||
* @param String $userName user name
|
||||
* @param array $otherUserAttrs user's first/last name
|
||||
* @return mixed true if ok, string with error message if not valid
|
||||
*/
|
||||
function checkPasswordStrength($password) {
|
||||
function checkPasswordStrength($password, $userName, $otherUserAttrs) {
|
||||
if ($password == null) {
|
||||
$password = "";
|
||||
}
|
||||
|
@ -347,22 +349,48 @@ function checkPasswordStrength($password) {
|
|||
$symbols++;
|
||||
}
|
||||
}
|
||||
$rulesMatched = 0;
|
||||
$rulesFailed = 0;
|
||||
// check lower case
|
||||
if ($lower < $cfg->passwordMinLower) {
|
||||
if (($cfg->checkedRulesCount == -1) && ($lower < $cfg->passwordMinLower)) {
|
||||
return sprintf(_('The password is too weak. You have to enter at least %s lower case characters.'), $cfg->passwordMinLower);
|
||||
}
|
||||
if ($lower < $cfg->passwordMinLower) {
|
||||
$rulesFailed++;
|
||||
}
|
||||
else {
|
||||
$rulesMatched++;
|
||||
}
|
||||
// check upper case
|
||||
if ($upper < $cfg->passwordMinUpper) {
|
||||
if (($cfg->checkedRulesCount == -1) && ($upper < $cfg->passwordMinUpper)) {
|
||||
return sprintf(_('The password is too weak. You have to enter at least %s upper case characters.'), $cfg->passwordMinUpper);
|
||||
}
|
||||
if ($upper < $cfg->passwordMinUpper) {
|
||||
$rulesFailed++;
|
||||
}
|
||||
else {
|
||||
$rulesMatched++;
|
||||
}
|
||||
// check numeric
|
||||
if ($numeric < $cfg->passwordMinNumeric) {
|
||||
if (($cfg->checkedRulesCount == -1) && ($numeric < $cfg->passwordMinNumeric)) {
|
||||
return sprintf(_('The password is too weak. You have to enter at least %s numeric characters.'), $cfg->passwordMinNumeric);
|
||||
}
|
||||
if ($numeric < $cfg->passwordMinNumeric) {
|
||||
$rulesFailed++;
|
||||
}
|
||||
else {
|
||||
$rulesMatched++;
|
||||
}
|
||||
// check symbols
|
||||
if ($symbols < $cfg->passwordMinSymbol) {
|
||||
if (($cfg->checkedRulesCount == -1) && ($symbols < $cfg->passwordMinSymbol)) {
|
||||
return sprintf(_('The password is too weak. You have to enter at least %s symbolic characters.'), $cfg->passwordMinSymbol);
|
||||
}
|
||||
if ($symbols < $cfg->passwordMinSymbol) {
|
||||
$rulesFailed++;
|
||||
}
|
||||
else {
|
||||
$rulesMatched++;
|
||||
}
|
||||
// check classes
|
||||
$classes = 0;
|
||||
if ($lower > 0) {
|
||||
|
@ -377,9 +405,51 @@ function checkPasswordStrength($password) {
|
|||
if ($symbols > 0) {
|
||||
$classes++;
|
||||
}
|
||||
if ($classes < $cfg->passwordMinClasses) {
|
||||
if (($cfg->checkedRulesCount == -1) && ($classes < $cfg->passwordMinClasses)) {
|
||||
return sprintf(_('The password is too weak. You have to enter at least %s different character classes (upper/lower case, numbers and symbols).'), $cfg->passwordMinClasses);
|
||||
}
|
||||
if ($classes < $cfg->passwordMinClasses) {
|
||||
$rulesFailed++;
|
||||
}
|
||||
else {
|
||||
$rulesMatched++;
|
||||
}
|
||||
// check rules count
|
||||
if (($cfg->checkedRulesCount != -1) && ($rulesMatched < $cfg->checkedRulesCount)) {
|
||||
return sprintf(_('The password is too weak. It needs to match at least %s password complexity rules.'), $cfg->checkedRulesCount);
|
||||
}
|
||||
// check user name
|
||||
if (($cfg->passwordMustNotContainUser == 'true') && !empty($userName)) {
|
||||
$pwdLow = strtolower($password);
|
||||
$userLow = strtolower($userName);
|
||||
if (strpos($pwdLow, $userLow) !== false) {
|
||||
return _('The password is too weak. You may not use the user name as part of the password.');
|
||||
}
|
||||
}
|
||||
// check part of user name and additional attributes
|
||||
if (($cfg->passwordMustNotContain3Chars == 'true') && (!empty($userName) || !empty($otherUserAttrs))) {
|
||||
$pwdLow = strtolower($password);
|
||||
// check if contains part of user name
|
||||
if (!empty($userName) && (strlen($userName) > 2)) {
|
||||
$userLow = strtolower($userName);
|
||||
for ($i = 0; $i < strlen($userLow) - 3; $i++) {
|
||||
$part = substr($userLow, 0, 3);
|
||||
if (strpos($pwdLow, $part) !== false) {
|
||||
return _('The password is too weak. You may not use parts of the user name for the password.');
|
||||
}
|
||||
}
|
||||
}
|
||||
// check other attributes
|
||||
foreach ($otherUserAttrs as $other) {
|
||||
$low = strtolower($other);
|
||||
for ($i = 0; $i < strlen($low) - 3; $i++) {
|
||||
$part = substr($low, 0, 3);
|
||||
if (strpos($pwdLow, $part) !== false) {
|
||||
return _('The password is too weak. You may not use parts of user attributes for the password.');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -153,6 +153,9 @@ if (isset($_POST['submitFormData'])) {
|
|||
$cfg->passwordMinNumeric = $_POST['passwordMinNumeric'];
|
||||
$cfg->passwordMinSymbol = $_POST['passwordMinSymbol'];
|
||||
$cfg->passwordMinClasses = $_POST['passwordMinClasses'];
|
||||
$cfg->checkedRulesCount = $_POST['passwordRulesCount'];
|
||||
$cfg->passwordMustNotContain3Chars = isset($_POST['passwordMustNotContain3Chars']) && ($_POST['passwordMustNotContain3Chars'] == 'on') ? 'true' : 'false';
|
||||
$cfg->passwordMustNotContainUser = isset($_POST['passwordMustNotContainUser']) && ($_POST['passwordMustNotContainUser'] == 'on') ? 'true' : 'false';
|
||||
if (isset($_POST['sslCaCertUpload'])) {
|
||||
if (!isset($_FILES['sslCaCert']) || ($_FILES['sslCaCert']['size'] == 0)) {
|
||||
$errors[] = _('No file selected.');
|
||||
|
@ -381,11 +384,21 @@ $policyTable = new htmlTable();
|
|||
$options20 = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20);
|
||||
$options4 = array(0, 1, 2, 3, 4);
|
||||
$policyTable->addElement(new htmlTableExtendedSelect('passwordMinLength', $options20, array($cfg->passwordMinLength), _('Minimum password length'), '242'), true);
|
||||
$policyTable->addVerticalSpace('5px');
|
||||
$policyTable->addElement(new htmlTableExtendedSelect('passwordMinLower', $options20, array($cfg->passwordMinLower), _('Minimum lowercase characters'), '242'), true);
|
||||
$policyTable->addElement(new htmlTableExtendedSelect('passwordMinUpper', $options20, array($cfg->passwordMinUpper), _('Minimum uppercase characters'), '242'), true);
|
||||
$policyTable->addElement(new htmlTableExtendedSelect('passwordMinNumeric', $options20, array($cfg->passwordMinNumeric), _('Minimum numeric characters'), '242'), true);
|
||||
$policyTable->addElement(new htmlTableExtendedSelect('passwordMinSymbol', $options20, array($cfg->passwordMinSymbol), _('Minimum symbolic characters'), '242'), true);
|
||||
$policyTable->addElement(new htmlTableExtendedSelect('passwordMinClasses', $options4, array($cfg->passwordMinClasses), _('Minimum character classes'), '242'), true);
|
||||
$policyTable->addVerticalSpace('5px');
|
||||
$rulesCountOptions = array(_('all') => '-1', '3' => '3', '4' => '4');
|
||||
$rulesCountSelect = new htmlTableExtendedSelect('passwordRulesCount', $rulesCountOptions, array($cfg->checkedRulesCount), _('Number of rules that must match'), '246');
|
||||
$rulesCountSelect->setHasDescriptiveElements(true);
|
||||
$policyTable->addElement($rulesCountSelect, true);
|
||||
$passwordMustNotContainUser = ($cfg->passwordMustNotContainUser === 'true') ? true : false;
|
||||
$policyTable->addElement(new htmlTableExtendedInputCheckbox('passwordMustNotContainUser',$passwordMustNotContainUser , _('Password must not contain user name'), '247'), true);
|
||||
$passwordMustNotContain3Chars = ($cfg->passwordMustNotContain3Chars === 'true') ? true : false;
|
||||
$policyTable->addElement(new htmlTableExtendedInputCheckbox('passwordMustNotContain3Chars', $passwordMustNotContain3Chars, _('Password must not contain part of user/first/last name'), '248'), true);
|
||||
$container->addElement($policyTable, true);
|
||||
$container->addElement(new htmlSpacer(null, '10px'), true);
|
||||
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
$Id$
|
||||
|
||||
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
|
||||
Copyright (C) 2014 Roland Gruber
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
*/
|
||||
<?php
|
||||
|
||||
$_SERVER ['REMOTE_ADDR'] = '127.0.0.1';
|
||||
|
||||
include_once (dirname ( __FILE__ ) . '/../utils/configuration.inc');
|
||||
include_once (dirname ( __FILE__ ) . '/../../lib/security.inc');
|
||||
|
||||
/**
|
||||
* Checks password checking functions.
|
||||
*
|
||||
* @author Roland Gruber
|
||||
*
|
||||
*/
|
||||
class SecurityTest extends PHPUnit_Framework_TestCase {
|
||||
|
||||
private $cfg = null;
|
||||
|
||||
protected function setUp() {
|
||||
testCreateDefaultConfig ();
|
||||
$this->cfg = &$_SESSION ['cfgMain'];
|
||||
$this->resetPasswordRules();
|
||||
}
|
||||
|
||||
public function testMinLength() {
|
||||
$this->cfg->passwordMinLength = 5;
|
||||
$this->checkPwd(array('55555', '666666'), array('1', '22', '333', '4444'));
|
||||
}
|
||||
|
||||
public function testMinUpper() {
|
||||
$this->cfg->passwordMinUpper = 3;
|
||||
$this->checkPwd(array('55A5AA55', '6BB666BB66', 'ABC'), array ('1A', '2C2C', 'AB3', '44BB'));
|
||||
}
|
||||
|
||||
public function testMinLower() {
|
||||
$this->cfg->passwordMinLower = 3;
|
||||
$this->checkPwd(array('55a5aa55', '6bb666bb66', 'abc'), array ('1a', '2c2c', 'ab3', '44bbABC'));
|
||||
}
|
||||
|
||||
public function testMinNumeric() {
|
||||
$this->cfg->passwordMinNumeric = 3;
|
||||
$this->checkPwd(array('333', '4444'), array('1', '22', '33A', '44bb'));
|
||||
}
|
||||
|
||||
public function testMinSymbol() {
|
||||
$this->cfg->passwordMinSymbol = 3;
|
||||
$this->checkPwd(array('---', '++++'), array('1.', '2.2.', '3+3+A', '44bb'));
|
||||
}
|
||||
|
||||
public function testMinClasses() {
|
||||
$this->cfg->passwordMinClasses = 3;
|
||||
$this->checkPwd(array('aB.', 'aB.1', 'aa.B99'), array('1', '2.', '3+-', '44bb'));
|
||||
}
|
||||
|
||||
public function testRulesCount() {
|
||||
$this->cfg->passwordMinUpper = 3;
|
||||
$this->cfg->passwordMinLower = 3;
|
||||
$this->cfg->passwordMinNumeric = 3;
|
||||
$this->cfg->passwordMinSymbol = 3;
|
||||
$this->cfg->passwordMinClasses = 3;
|
||||
// all rules
|
||||
$this->cfg->checkedRulesCount = -1;
|
||||
$this->checkPwd(array('ABC---abc123', 'ABC123xxx.-.-'), array('1', '2.', '3+-', '44bb', 'ABCabc---22'));
|
||||
// at least 3 rules
|
||||
$this->cfg->checkedRulesCount = 3;
|
||||
$this->checkPwd(array('ABC---abc', 'ABC123.-.-', 'ABCabc-'), array('1', '2.', '3+-', '44bb', 'ABC--22'));
|
||||
}
|
||||
|
||||
public function testUser() {
|
||||
$this->cfg->passwordMustNotContainUser = 'true';
|
||||
$this->checkPwd(array('u', 'us', 'use', 'use1r'), array('user', '2user', 'user3'), 'user');
|
||||
}
|
||||
|
||||
public function testUserAttributes() {
|
||||
$this->cfg->passwordMustNotContain3Chars = 'true';
|
||||
$this->checkPwd(array('u', 'us', 'us1e', 'us1er'), array('use', 'user', '2user', 'user3'), 'user');
|
||||
$this->checkPwd(
|
||||
array('uf', 'usfi', 'us1ela3s', 'us1er.la#st'),
|
||||
array('use', 'user', '2user', 'user3', 'las', 'last', 'fir', 'first'),
|
||||
'user',
|
||||
array('first', 'last'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the password rules to do no checks at all.
|
||||
*/
|
||||
private function resetPasswordRules() {
|
||||
$this->cfg->passwordMinLength = 0;
|
||||
$this->cfg->passwordMinUpper = 0;
|
||||
$this->cfg->passwordMinLower = 0;
|
||||
$this->cfg->passwordMinNumeric = 0;
|
||||
$this->cfg->passwordMinSymbol = 0;
|
||||
$this->cfg->passwordMinClasses = 0;
|
||||
$this->cfg->checkedRulesCount = -1;
|
||||
$this->cfg->passwordMustNotContainUser = 'false';
|
||||
$this->cfg->passwordMustNotContain3Chars = 'false';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given passwords are correctly accepted/rejected.
|
||||
*
|
||||
* @param array $pwdsToAccept passwords that must be accepted
|
||||
* @param array $pwdsToReject passwords that must be rejected
|
||||
* @param String $userName user name
|
||||
* @param array $otherUserAttrs other user attributes to check
|
||||
*/
|
||||
private function checkPwd($pwdsToAccept, $pwdsToReject, $userName = null, $otherUserAttrs = null) {
|
||||
if ($userName == null) {
|
||||
$userName = 'username';
|
||||
}
|
||||
if ($otherUserAttrs == null) {
|
||||
$otherUserAttrs = array ();
|
||||
}
|
||||
foreach ($pwdsToAccept as $pwd) {
|
||||
$this->assertTrue(checkPasswordStrength($pwd, $userName, $otherUserAttrs));
|
||||
}
|
||||
foreach ($pwdsToReject as $pwd) {
|
||||
$this->assertNotTrue(checkPasswordStrength($pwd, $userName, $otherUserAttrs));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
$Id$
|
||||
|
||||
This code is part of LDAP Account Manager (http://www.ldap-account-manager.org/)
|
||||
Copyright (C) 2014 Roland Gruber
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
*/
|
||||
<?php
|
||||
|
||||
include_once (dirname(__FILE__) . '/../../lib/config.inc');
|
||||
|
||||
/**
|
||||
* Creates a default configuration and adds it to $_SESSION['cfgMain'] and $_SESSION['config'].
|
||||
*/
|
||||
function testCreateDefaultConfig() {
|
||||
$cfgMain = new LAMCfgMain();
|
||||
$cfgMain->logDestination = 'NONE';
|
||||
$_SESSION['cfgMain'] = $cfgMain;
|
||||
$cfgPath = dirname(__FILE__) . '/../../config/phpunit.conf';
|
||||
if (file_exists($cfgPath)) {
|
||||
unlink($cfgPath);
|
||||
}
|
||||
touch($cfgPath);
|
||||
$config = new LAMConfig('phpunit');
|
||||
$_SESSION['config'] = $config;
|
||||
}
|
||||
|
||||
?>
|
Loading…
Reference in New Issue