switch from crypt() to password_hash() and password_verify(), thx to kapsonfire for the hint
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
This commit is contained in:
@@ -609,7 +609,7 @@ opcache.validate_timestamps'),
|
|||||||
('system', 'apache24', '1'),
|
('system', 'apache24', '1'),
|
||||||
('system', 'apache24_ocsp_cache_path', 'shmcb:/var/run/apache2/ocsp-stapling.cache(131072)'),
|
('system', 'apache24_ocsp_cache_path', 'shmcb:/var/run/apache2/ocsp-stapling.cache(131072)'),
|
||||||
('system', 'documentroot_use_default_value', '0'),
|
('system', 'documentroot_use_default_value', '0'),
|
||||||
('system', 'passwordcryptfunc', '3'),
|
('system', 'passwordcryptfunc', '2y'),
|
||||||
('system', 'axfrservers', ''),
|
('system', 'axfrservers', ''),
|
||||||
('system', 'powerdns_mode', 'Native'),
|
('system', 'powerdns_mode', 'Native'),
|
||||||
('system', 'customer_ssl_path', '/etc/ssl/froxlor-custom/'),
|
('system', 'customer_ssl_path', '/etc/ssl/froxlor-custom/'),
|
||||||
|
|||||||
@@ -420,30 +420,6 @@ class FroxlorInstall
|
|||||||
$content[] = $check;
|
$content[] = $check;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* generate safe unique token
|
|
||||||
*
|
|
||||||
* @param int $length
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private function genUniqueToken(int $length = 16)
|
|
||||||
{
|
|
||||||
if (!isset($length) || intval($length) <= 8) {
|
|
||||||
$length = 16;
|
|
||||||
}
|
|
||||||
if (function_exists('random_bytes')) {
|
|
||||||
return bin2hex(random_bytes($length));
|
|
||||||
}
|
|
||||||
if (function_exists('mcrypt_create_iv')) {
|
|
||||||
return bin2hex(mcrypt_create_iv($length, MCRYPT_DEV_URANDOM));
|
|
||||||
}
|
|
||||||
if (function_exists('openssl_random_pseudo_bytes')) {
|
|
||||||
return bin2hex(openssl_random_pseudo_bytes($length));
|
|
||||||
}
|
|
||||||
// if everything else fails, use unsafe fallback
|
|
||||||
return substr(md5(uniqid(microtime(), 1)), 0, $length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create corresponding entries in froxlor database
|
* create corresponding entries in froxlor database
|
||||||
*
|
*
|
||||||
@@ -513,8 +489,8 @@ class FroxlorInstall
|
|||||||
];
|
];
|
||||||
$ins_data = array(
|
$ins_data = array(
|
||||||
'loginname' => $this->_data['admin_user'],
|
'loginname' => $this->_data['admin_user'],
|
||||||
/* use SHA256 default crypt */
|
/* use system default crypt */
|
||||||
'password' => crypt($this->_data['admin_pass1'], '$5$' . $this->genUniqueToken() . $this->genUniqueToken()),
|
'password' => password_hash($this->_data['admin_pass1'], PASSWORD_DEFAULT),
|
||||||
'email' => 'admin@' . $this->_data['servername'],
|
'email' => 'admin@' . $this->_data['servername'],
|
||||||
'deflang' => $this->_languages[$this->_activelng]
|
'deflang' => $this->_languages[$this->_activelng]
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -87,6 +87,11 @@ if (\Froxlor\Froxlor::isFroxlorVersion('0.10.99')) {
|
|||||||
Settings::AddNew("panel.settings_mode", $panel_settings_mode);
|
Settings::AddNew("panel.settings_mode", $panel_settings_mode);
|
||||||
lastStepStatus(0);
|
lastStepStatus(0);
|
||||||
|
|
||||||
|
showUpdateStep("Adjusting existing settings");
|
||||||
|
Settings::Set('system.passwordcryptfunc', PASSWORD_DEFAULT);
|
||||||
|
lastStepStatus(0);
|
||||||
|
|
||||||
|
|
||||||
if (\Froxlor\Froxlor::isFroxlorVersion('0.10.99')) {
|
if (\Froxlor\Froxlor::isFroxlorVersion('0.10.99')) {
|
||||||
showUpdateStep("Updating from 0.10.99 to 0.11.0-dev1", false);
|
showUpdateStep("Updating from 0.10.99 to 0.11.0-dev1", false);
|
||||||
\Froxlor\Froxlor::updateToVersion('0.11.0-dev1');
|
\Froxlor\Froxlor::updateToVersion('0.11.0-dev1');
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Froxlor\System;
|
namespace Froxlor\System;
|
||||||
|
|
||||||
use Froxlor\Settings;
|
use Froxlor\Settings;
|
||||||
@@ -8,11 +9,8 @@ class Crypt
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a random password
|
* Generates a random password
|
||||||
*
|
|
||||||
* @param boolean $isSalt
|
|
||||||
* optional, create a hash for a salt used in \Froxlor\System\Crypt::makeCryptPassword because crypt() does not like some special characters in its salts, default is false
|
|
||||||
*/
|
*/
|
||||||
public static function generatePassword($isSalt = false)
|
public static function generatePassword()
|
||||||
{
|
{
|
||||||
$alpha_lower = 'abcdefghijklmnopqrstuvwxyz';
|
$alpha_lower = 'abcdefghijklmnopqrstuvwxyz';
|
||||||
$alpha_upper = strtoupper($alpha_lower);
|
$alpha_upper = strtoupper($alpha_lower);
|
||||||
@@ -31,11 +29,11 @@ class Crypt
|
|||||||
$pw .= mb_substr(self::specialShuffle($numeric), 0, $n);
|
$pw .= mb_substr(self::specialShuffle($numeric), 0, $n);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings::Get('panel.password_special_char_required') && ! $isSalt) {
|
if (Settings::Get('panel.password_special_char_required')) {
|
||||||
$pw .= mb_substr(self::specialShuffle($special), 0, $n);
|
$pw .= mb_substr(self::specialShuffle($special), 0, $n);
|
||||||
}
|
}
|
||||||
|
|
||||||
$pw = mb_substr($pw, - $length);
|
$pw = mb_substr($pw, -$length);
|
||||||
|
|
||||||
return self::specialShuffle($pw);
|
return self::specialShuffle($pw);
|
||||||
}
|
}
|
||||||
@@ -51,7 +49,7 @@ class Crypt
|
|||||||
{
|
{
|
||||||
$len = mb_strlen($str);
|
$len = mb_strlen($str);
|
||||||
$sploded = array();
|
$sploded = array();
|
||||||
while ($len -- > 0) {
|
while ($len-- > 0) {
|
||||||
$sploded[] = mb_substr($str, $len, 1);
|
$sploded[] = mb_substr($str, $len, 1);
|
||||||
}
|
}
|
||||||
shuffle($sploded);
|
shuffle($sploded);
|
||||||
@@ -83,33 +81,12 @@ class Crypt
|
|||||||
if ($htpasswd) {
|
if ($htpasswd) {
|
||||||
return '{SHA}' . base64_encode(sha1($password, true));
|
return '{SHA}' . base64_encode(sha1($password, true));
|
||||||
}
|
}
|
||||||
|
$algo = Settings::Get('system.passwordcryptfunc') !== null ? Settings::Get('system.passwordcryptfunc') : PASSWORD_DEFAULT;
|
||||||
$type = Settings::Get('system.passwordcryptfunc') !== null ? (int) Settings::Get('system.passwordcryptfunc') : 3;
|
return password_hash($password, $algo);
|
||||||
|
|
||||||
switch ($type) {
|
|
||||||
case 1:
|
|
||||||
$cryptPassword = crypt($password, '$1$' . self::generatePassword(true) . self::generatePassword(true));
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
// Blowfish hashing with a salt as follows: "$2a$", "$2x$" or "$2y$",
|
|
||||||
// a two digit cost parameter, "$", and 22 characters from the alphabet "./0-9A-Za-z"
|
|
||||||
$cryptPassword = crypt($password, '$2y$07$' . substr(self::generatePassword(true) . self::generatePassword(true) . self::generatePassword(true), 0, 22));
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
$cryptPassword = crypt($password, '$5$' . self::generatePassword(true) . self::generatePassword(true));
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
$cryptPassword = crypt($password, '$6$' . self::generatePassword(true) . self::generatePassword(true));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$cryptPassword = crypt($password, self::generatePassword(true) . self::generatePassword(true));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return $cryptPassword;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* return an array of available hashes for the crypt() function
|
* return an array of available hashes
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
@@ -119,19 +96,16 @@ class Crypt
|
|||||||
|
|
||||||
// get available pwd-hases
|
// get available pwd-hases
|
||||||
$available_pwdhashes = array(
|
$available_pwdhashes = array(
|
||||||
0 => $lng['serversettings']['systemdefault']
|
PASSWORD_DEFAULT => $lng['serversettings']['systemdefault']
|
||||||
);
|
);
|
||||||
if (defined('CRYPT_MD5') && CRYPT_MD5 == 1) {
|
if (defined('PASSWORD_BCRYPT')) {
|
||||||
$available_pwdhashes[1] = 'MD5';
|
$available_pwdhashes[PASSWORD_BCRYPT] = 'Bcrypt/Blowfish';
|
||||||
}
|
}
|
||||||
if (defined('CRYPT_BLOWFISH') && CRYPT_BLOWFISH == 1) {
|
if (defined('PASSWORD_ARGON2I')) {
|
||||||
$available_pwdhashes[2] = 'BLOWFISH';
|
$available_pwdhashes[PASSWORD_ARGON2I] = 'Argon2i';
|
||||||
}
|
}
|
||||||
if (defined('CRYPT_SHA256') && CRYPT_SHA256 == 1) {
|
if (defined('PASSWORD_ARGON2ID')) {
|
||||||
$available_pwdhashes[3] = 'SHA-256';
|
$available_pwdhashes[PASSWORD_ARGON2ID] = 'Argon2id';
|
||||||
}
|
|
||||||
if (defined('CRYPT_SHA512') && CRYPT_SHA512 == 1) {
|
|
||||||
$available_pwdhashes[4] = 'SHA-512';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $available_pwdhashes;
|
return $available_pwdhashes;
|
||||||
@@ -196,36 +170,27 @@ class Crypt
|
|||||||
*/
|
*/
|
||||||
public static function validatePasswordLogin($userinfo = null, $password = null, $table = 'panel_customers', $uid = 'customerid')
|
public static function validatePasswordLogin($userinfo = null, $password = null, $table = 'panel_customers', $uid = 'customerid')
|
||||||
{
|
{
|
||||||
$systype = 3; // SHA256
|
$algo = Settings::Get('system.passwordcryptfunc') !== null ? Settings::Get('system.passwordcryptfunc') : PASSWORD_DEFAULT;
|
||||||
if (Settings::Get('system.passwordcryptfunc') !== null) {
|
if (is_numeric($algo)) {
|
||||||
$systype = (int) Settings::Get('system.passwordcryptfunc');
|
// old setting format
|
||||||
|
$algo = PASSWORD_DEFAULT;
|
||||||
|
Settings::Set('system.passwordcryptfunc', $algo);
|
||||||
}
|
}
|
||||||
|
|
||||||
$pwd_hash = $userinfo['password'];
|
$pwd_hash = $userinfo['password'];
|
||||||
|
|
||||||
$update_hash = false;
|
$update_hash = false;
|
||||||
|
$pwd_check = "";
|
||||||
// check for good'ole md5
|
// check for good'ole md5
|
||||||
if (strlen($pwd_hash) == 32 && ctype_xdigit($pwd_hash)) {
|
if (strlen($pwd_hash) == 32 && ctype_xdigit($pwd_hash)) {
|
||||||
$pwd_check = md5($password);
|
$pwd_check = md5($password);
|
||||||
$update_hash = true;
|
$update_hash = true;
|
||||||
} else {
|
|
||||||
// cut out the salt from the hash
|
|
||||||
$pwd_salt = str_replace(substr(strrchr($pwd_hash, "$"), 1), "", $pwd_hash);
|
|
||||||
// create same hash to compare
|
|
||||||
$pwd_check = crypt($password, $pwd_salt);
|
|
||||||
// check whether the hash needs to be updated
|
|
||||||
$hash_type_chk = substr($pwd_hash, 0, 3);
|
|
||||||
// MD5 || BLOWFISH || SHA256 || SHA512
|
|
||||||
if (($systype == 1 && $hash_type_chk != '$1$') || ($systype == 2 && $hash_type_chk != '$2$') || ($systype == 3 && $hash_type_chk != '$5$') || ($systype == 4 && $hash_type_chk != '$6$')) {
|
|
||||||
$update_hash = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($pwd_hash == $pwd_check) {
|
if ($pwd_hash == $pwd_check || password_verify($password, $pwd_hash)) {
|
||||||
|
|
||||||
// check for update of hash (only if our database is ready to handle the bigger string)
|
// check for update of hash (only if our database is ready to handle the bigger string)
|
||||||
$is_ready = (\Froxlor\Froxlor::versionCompare2("0.9.33", \Froxlor\Froxlor::getVersion()) <= 0 ? true : false);
|
$is_ready = (\Froxlor\Froxlor::versionCompare2("0.9.33", \Froxlor\Froxlor::getVersion()) <= 0 ? true : false);
|
||||||
if ($update_hash && $is_ready) {
|
if ((password_needs_rehash($pwd_hash, $algo) || $update_hash) && $is_ready) {
|
||||||
$upd_stmt = \Froxlor\Database\Database::prepare("
|
$upd_stmt = \Froxlor\Database\Database::prepare("
|
||||||
UPDATE " . $table . " SET `password` = :newpasswd WHERE `" . $uid . "` = :uid
|
UPDATE " . $table . " SET `password` = :newpasswd WHERE `" . $uid . "` = :uid
|
||||||
");
|
");
|
||||||
@@ -235,7 +200,6 @@ class Crypt
|
|||||||
);
|
);
|
||||||
\Froxlor\Database\Database::pexecute($upd_stmt, $params);
|
\Froxlor\Database\Database::pexecute($upd_stmt, $params);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
Reference in New Issue
Block a user