Compare commits

...

2 Commits

Author SHA1 Message Date
Clemens Schwaighofer
e23389a7f8 Fix password re-hash in login with correct methods
Don't call the PHP functions directly, but use the internal wrapper
methods for password rehash check and set in Login class
2018-05-09 11:47:32 +09:00
Clemens Schwaighofer
c21e194eaf Add proper PHP password management
The old crypt based password methods are all deprecated and the new
password_* are now standard.

Also added auto rehash for old password on login
2018-05-09 11:34:40 +09:00
4 changed files with 114 additions and 29 deletions

View File

@@ -1,4 +1,5 @@
<?php <?php
$DEBUG_ALL_OVERRIDE = 0; // set to 1 to debug on live/remote server locations $DEBUG_ALL_OVERRIDE = 0; // set to 1 to debug on live/remote server locations
$DEBUG_ALL = 1; $DEBUG_ALL = 1;
$PRINT_ALL = 1; $PRINT_ALL = 1;

View File

@@ -294,10 +294,19 @@ class Login extends \CoreLibs\DB\IO
} elseif ((preg_match("/^\\$2(a|y)\\$/", $res['password']) || } elseif ((preg_match("/^\\$2(a|y)\\$/", $res['password']) ||
preg_match("/^\\$1\\$/", $res['password']) || preg_match("/^\\$1\\$/", $res['password']) ||
preg_match("/^\\$[0-9A-Za-z.]{12}$/", $res['password'])) && preg_match("/^\\$[0-9A-Za-z.]{12}$/", $res['password'])) &&
// old password have $07$ so we check this
preg_match("/\\$07\\$/", $res['password']) &&
!$this->verifyCryptString($this->password, $res['password']) !$this->verifyCryptString($this->password, $res['password'])
) { ) {
// check passwword as crypted, $2a$ or $2y$ is blowfish start, $1$ is MD5 start, $\w{12} is standard DES // check passwword as crypted, $2a$ or $2y$ is blowfish start, $1$ is MD5 start, $\w{12} is standard DES
// this is only for OLD $07$ password
$this->login_error = 1011; $this->login_error = 1011;
} elseif (preg_match("/^\\$2y\\$/", $res['password']) &&
!preg_match("/\\$07\\$/", $res['password']) &&
!$this->passwordVerify($this->password, $res['password'])
) {
// this is the new password hash methid, is only $2y$
$this->login_error = 1013;
} elseif (!preg_match("/^\\$2(a|y)\\$/", $res['password']) && } elseif (!preg_match("/^\\$2(a|y)\\$/", $res['password']) &&
!preg_match("/^\\$1\\$/", $res['password']) && !preg_match("/^\\$1\\$/", $res['password']) &&
!preg_match("/^\\$[0-9A-Za-z.]{12}$/", $res['password']) && !preg_match("/^\\$[0-9A-Za-z.]{12}$/", $res['password']) &&
@@ -306,6 +315,14 @@ class Login extends \CoreLibs\DB\IO
// check old plain password, non case sensitive // check old plain password, non case sensitive
$this->login_error = 1012; $this->login_error = 1012;
} else { } else {
// check if the current password is an invalid hash and do a rehash and set password
// $this->debug('LOGIN', 'Hash: '.$res['password'].' -> VERIFY: '.($this->passwordVerify($this->password, $res['password']) ? 'OK' : 'FAIL').' => HASH: '.($this->passwordRehashCheck($res['password']) ? 'NEW NEEDED' : 'OK'));
if ($this->passwordRehashCheck($res['password'])) {
$new_hash = $this->passwordSet($this->password);
// update password hash to new one now
$q = "UPDATE edit_user SET password = '".$this->dbEscapeString($new_hash)."' WHERE edit_user_id = ".$res['edit_user_id'];
$this->dbExec($q);
}
// normal user processing // normal user processing
// set class var and session var // set class var and session var
$_SESSION["EUID"] = $this->euid = $res["edit_user_id"]; $_SESSION["EUID"] = $this->euid = $res["edit_user_id"];
@@ -548,6 +565,7 @@ class Login extends \CoreLibs\DB\IO
$this->acl['admin'] = 1; $this->acl['admin'] = 1;
$this->acl['base'] = 100; $this->acl['base'] = 100;
} else { } else {
$this->acl['admin'] = 0;
// now go throw the flow and set the correct ACL // now go throw the flow and set the correct ACL
// user > page > group // user > page > group
// group ACL 0 // group ACL 0
@@ -816,6 +834,7 @@ class Login extends \CoreLibs\DB\IO
"1010" => $this->l->__("Fatal Error: <b>Login Failed - Wrong Username or Password</b>"), // user not found "1010" => $this->l->__("Fatal Error: <b>Login Failed - Wrong Username or Password</b>"), // user not found
"1011" => $this->l->__("Fatal Error: <b>Login Failed - Wrong Username or Password</b>"), // blowfish password wrong "1011" => $this->l->__("Fatal Error: <b>Login Failed - Wrong Username or Password</b>"), // blowfish password wrong
"1012" => $this->l->__("Fatal Error: <b>Login Failed - Wrong Username or Password</b>"), // fallback md5 password wrong "1012" => $this->l->__("Fatal Error: <b>Login Failed - Wrong Username or Password</b>"), // fallback md5 password wrong
"1013" => $this->l->__("Fatal Error: <b>Login Failed - Wrong Username or Password</b>"), // new password_hash wrong
"102" => $this->l->__("Fatal Error: <b>Login Failed - Please enter username and password</b>"), "102" => $this->l->__("Fatal Error: <b>Login Failed - Please enter username and password</b>"),
"103" => $this->l->__("Fatal Error: <b>You do not have the rights to access this Page</b>"), "103" => $this->l->__("Fatal Error: <b>You do not have the rights to access this Page</b>"),
"104" => $this->l->__("Fatal Error: <b>Login Failed - User not enabled</b>"), "104" => $this->l->__("Fatal Error: <b>Login Failed - User not enabled</b>"),

View File

@@ -148,12 +148,13 @@ class Basic
// error char for the char conver // error char for the char conver
public $mbErrorChar; public $mbErrorChar;
// crypt saslt prefix // [!!! DEPRECATED !!!] crypt saslt prefix
public $cryptSaltPrefix = ''; public $cryptSaltPrefix = '';
public $cryptSaltSuffix = ''; public $cryptSaltSuffix = '';
public $cryptIterationCost = 7; // this is for staying backwards compatible with the old ones public $cryptIterationCost = 7; // this is for staying backwards compatible with the old ones
public $cryptSaltSize = 22; // default 22 chars for blowfish, 2 for STD DES, 8 for MD5, public $cryptSaltSize = 22; // default 22 chars for blowfish, 2 for STD DES, 8 for MD5,
// new better password management
protected $password_options = array ();
// session name // session name
private $session_name = ''; private $session_name = '';
private $session_id = ''; private $session_id = '';
@@ -340,8 +341,10 @@ class Basic
$this->session_id = session_id(); $this->session_id = session_id();
} }
// init crypt settings // [!!! DEPRECATED !!!] init crypt settings
$this->cryptInit(); $this->cryptInit();
// new better password init
$this->passwordInit();
// start logging running time // start logging running time
$this->runningTime(); $this->runningTime();
@@ -1630,6 +1633,11 @@ class Basic
return false; return false;
} }
// [!!! DEPRECATED !!!]
// ALL crypt* methids are DEPRECATED and SHALL NOT BE USED
// use the new password* instead
// [!!! DEPRECATED !!!] -> passwordInit
// METHOD: cryptInit // METHOD: cryptInit
// PARAMS: none // PARAMS: none
// RETURN: none // RETURN: none
@@ -1676,6 +1684,7 @@ class Basic
} }
} }
// [!!! DEPRECATED !!!] -> not needed
// METHOD: cryptSaltString // METHOD: cryptSaltString
// PARAMS: random string length, default is 22 (for blowfish crypt) // PARAMS: random string length, default is 22 (for blowfish crypt)
// RETURN: random string // RETURN: random string
@@ -1703,6 +1712,7 @@ class Basic
return $salt_string; return $salt_string;
} }
// [!!! DEPRECATED !!!] -> passwordSet
// METHOD: cryptString // METHOD: cryptString
// PARAMS: string to be crypted (one way) // PARAMS: string to be crypted (one way)
// RETURN: encrypted string // RETURN: encrypted string
@@ -1714,6 +1724,7 @@ class Basic
return crypt($string, $this->cryptSaltPrefix.$this->cryptSaltString($this->cryptSaltSize).$this->cryptSaltSuffix); return crypt($string, $this->cryptSaltPrefix.$this->cryptSaltString($this->cryptSaltSize).$this->cryptSaltSuffix);
} }
// [!!! DEPRECATED !!!] -> passwordVerify
// METHOD: verifyCryptString // METHOD: verifyCryptString
// PARAMS: plain string (eg password) // PARAMS: plain string (eg password)
// full crypted string (from cryptString // full crypted string (from cryptString
@@ -1729,6 +1740,61 @@ class Basic
} }
} }
// *** BETTER PASSWORD OPTIONS, must be used ***
// METHOD: passwordInit
// PARAMS: none
// RETURN: none
// DESC : inits the password options set
// currently this is et empty, and the default options are used
private function passwordInit()
{
// set default password cost: use default set automatically
$this->password_options = array (
// 'cost' => PASSWORD_BCRYPT_DEFAULT_COST
);
}
// METHOD: passwordSet
// PARAMS: password
// RETURN: hashed password
// DESC : creates the password hash
public function passwordSet($password)
{
// always use the PHP default for the password
// password options ca be set in the password init, but should be kept as default
return password_hash($password, PASSWORD_DEFAULT, $this->password_options);
}
// METHOD: passwordVerify
// PARAMS: password and hash
// RETURN: true or false
// DESC : checks if the entered password matches the hash
public function passwordVerify($password, $hash)
{
if (password_verify($password, $hash)) {
return true;
} else {
return false;
}
// in case something strange, return false on default
return false;
}
// METHOD: passwordRehashCheck
// PARAMS: hash
// RETURN: true or false
// DESC : checks if the password needs to be rehashed
public function passwordRehashCheck($hash)
{
if (password_needs_rehash($hash, PASSWORD_DEFAULT, $this->password_options)) {
return true;
} else {
return false;
}
// in case of strange, force re-hash
return true;
}
// *** COLORS *** // *** COLORS ***
// METHOD: hex2rgb // METHOD: hex2rgb

View File

@@ -1145,7 +1145,6 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO
// DESC : save a table, reference and all input fields // DESC : save a table, reference and all input fields
public function formSaveTableArray($addslashes = 0) public function formSaveTableArray($addslashes = 0)
{ {
// global $_FILES;
// for drop_down_db_input check if text field is filled and if, if not yet in db ... // for drop_down_db_input check if text field is filled and if, if not yet in db ...
// and upload files // and upload files
if (!is_array($this->table_array)) { if (!is_array($this->table_array)) {
@@ -1240,8 +1239,8 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO
// for password crypt it as blowfish, or if not available MD5 // for password crypt it as blowfish, or if not available MD5
if ($this->table_array[$key]['type'] == 'password') { if ($this->table_array[$key]['type'] == 'password') {
if ($this->table_array[$key]["value"]) { if ($this->table_array[$key]["value"]) {
// password is stored in blowfish format, or in the format supported by this PHP version // use the better new passwordSet instead of crypt based
$this->table_array[$key]["value"] = $this->cryptString($this->table_array[$key]["value"]); $this->table_array[$key]['value'] = $this->passwordSet($this->table_array[$key]['value']);
$this->table_array[$key]["HIDDEN_value"] = $this->table_array[$key]["value"]; $this->table_array[$key]["HIDDEN_value"] = $this->table_array[$key]["value"];
} else { } else {
// $this->table_array[$key]["HIDDEN_value"] = // $this->table_array[$key]["HIDDEN_value"] =