From 03bc1a45e3c301c9415cfb19648049235e9758cc Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 24 Sep 2015 11:49:22 +0900 Subject: [PATCH 1/3] PHP7 and error handling improvements to random salt generator --- classes/security/Password.class.php | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/classes/security/Password.class.php b/classes/security/Password.class.php index 436c2b6e5..049e9b276 100644 --- a/classes/security/Password.class.php +++ b/classes/security/Password.class.php @@ -246,8 +246,13 @@ class Password $entropy_capped_bytes = min(32, $entropy_required_bytes); // Find and use the most secure way to generate a random string + $entropy = false; $is_windows = (defined('PHP_OS') && strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'); - if(function_exists('openssl_random_pseudo_bytes') && (!$is_windows || version_compare(PHP_VERSION, '5.4', '>='))) + if(function_exists('random_bytes')) // PHP 7 + { + $entropy = random_bytes($capped_entropy_bytes); + } + elseif(function_exists('openssl_random_pseudo_bytes') && (!$is_windows || version_compare(PHP_VERSION, '5.4', '>='))) { $entropy = openssl_random_pseudo_bytes($entropy_capped_bytes); } @@ -262,10 +267,16 @@ class Password elseif(!$is_windows && @is_readable('/dev/urandom')) { $fp = fopen('/dev/urandom', 'rb'); + if (function_exists('stream_set_read_buffer')) // This function does not exist in HHVM + { + stream_set_read_buffer($fp, 0); // Prevent reading several KB of unnecessary data from urandom + } $entropy = fread($fp, $entropy_capped_bytes); fclose($fp); } - else + + // Use built-in source of entropy if an error occurs while using other functions + if($entropy === false || strlen($entropy) < $entropy_capped_bytes) { $entropy = ''; for($i = 0; $i < $entropy_capped_bytes; $i += 2) From fece1fbc18b656a0c98470f2ca1d4f801c2e8caa Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 24 Sep 2015 12:09:22 +0900 Subject: [PATCH 2/3] Support additional check-only algorithms in Password class --- classes/security/Password.class.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/classes/security/Password.class.php b/classes/security/Password.class.php index 049e9b276..1363eaec7 100644 --- a/classes/security/Password.class.php +++ b/classes/security/Password.class.php @@ -162,6 +162,10 @@ class Password return $this->strcmpConstantTime($hash_to_compare, $hash); default: + if(in_array($algorithm, hash_algos())) + { + return $this->strcmpConstantTime(hash($algorithm, $password), $hash); + } return false; } } @@ -185,6 +189,22 @@ class Password { return 'md5'; } + elseif(strlen($hash) === 40 && ctype_xdigit($hash)) + { + return 'sha1'; + } + elseif(strlen($hash) === 64 && ctype_xdigit($hash)) + { + return 'sha256'; + } + elseif(strlen($hash) === 96 && ctype_xdigit($hash)) + { + return 'sha384'; + } + elseif(strlen($hash) === 128 && ctype_xdigit($hash)) + { + return 'sha512'; + } elseif(strlen($hash) === 16 && ctype_xdigit($hash)) { return 'mysql_old_password'; From 804dd3073a7b7e8ca5b1ceeb3b929d184f9125fd Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Mon, 21 Dec 2015 13:44:46 +0900 Subject: [PATCH 3/3] Support custom (user-defined) check-only algorithms in Password class --- classes/security/Password.class.php | 32 +++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/classes/security/Password.class.php b/classes/security/Password.class.php index 1363eaec7..0f5d5afe6 100644 --- a/classes/security/Password.class.php +++ b/classes/security/Password.class.php @@ -12,6 +12,24 @@ */ class Password { + /** + * @brief Custom algorithms are stored here + * @var array + */ + protected static $_custom = array(); + + /** + * @brief Register a custom algorithm for password checking + * @param string $name The name of the algorithm + * @param string $regexp The regular expression to detect the algorithm + * @param callable $callback The function to call to regenerate the hash + * @return void + */ + public static function registerCustomAlgorithm($name, $regexp, $callback) + { + self::$_custom[$name] = array('regexp' => $regexp, 'callback' => $callback); + } + /** * @brief Return the list of hashing algorithms supported by this server * @return array @@ -162,6 +180,12 @@ class Password return $this->strcmpConstantTime($hash_to_compare, $hash); default: + if($algorithm && isset(self::$_custom[$algorithm])) + { + $hash_callback = self::$_custom[$algorithm]['callback']; + $hash_to_compare = $hash_callback($password, $hash); + return $this->strcmpConstantTime($hash_to_compare, $hash); + } if(in_array($algorithm, hash_algos())) { return $this->strcmpConstantTime(hash($algorithm, $password), $hash); @@ -177,6 +201,14 @@ class Password */ function checkAlgorithm($hash) { + foreach(self::$_custom as $name => $definition) + { + if(preg_match($definition['regexp'], $hash)) + { + return $name; + } + } + if(preg_match('/^\$2[axy]\$([0-9]{2})\$/', $hash, $matches)) { return 'bcrypt';