bannedPasswords = self::$BANLIST; foreach ($options as $opt => $val) { if ($opt === 'banmode') { trigger_error('The lowercase banmode option is deprecated. Use banMode instead.', E_USER_DEPRECATED); $opt = 'banMode'; } $this->{$opt} = $val; } } /** * Checks if a password is strong enough for use on a live site. Used to check the front-end Secret Word. * * @param string $password The password to check * @param bool $throwExceptions Throw an exception if the password is not strong enough? * * @return bool */ public static function isStrongEnough($password, $throwExceptions = true) { $complexify = new self(); $res = (object) [ 'valid' => strlen($password) >= 32, 'complexity' => 50, 'errors' => (strlen($password) >= 32) ? [] : ['tooshort'], ]; if (function_exists('mb_strlen') && function_exists('mb_convert_encoding') && function_exists('mb_substr') && function_exists('mb_convert_case')) { $res = $complexify->evaluateSecurity($password); } if ($res->valid) { return true; } if (!$throwExceptions) { return false; } $error = count($res->errors) ? array_shift($res->errors) : 'toosimple'; $errorMessage = Platform::getInstance()->translate('COM_AKEEBA_CPANEL_ERR_FESECRETWORD_' . $error); throw new RuntimeException($errorMessage, 403); } /** * Check the complexity of a password * * @param string $password The password to check * * @return object StdClass object with properties "valid", "complexity", and "error" * - valid: TRUE if the password is complex enough, FALSE if it is not * - complexity: The complexity of the password as a percent * - errors: Array containing descriptions of what made the password fail. Possible values are: banned, toosimple, * tooshort */ public function evaluateSecurity($password) { $complexity = 0; $error = []; // Reset complexity to 0 when banned password is found if (!$this->inBanlist($password)) { // Add character complexity foreach (self::$CHARSETS as $charset) { $complexity += $this->additionalComplexityForCharset($password, $charset); } } else { array_push($error, 'banned'); $complexity = 1; } // Use natural log to produce linear scale $complexity = log($complexity ** mb_strlen($password, $this->encoding)) * (1 / $this->strengthScaleFactor); if ($complexity <= self::$MIN_COMPLEXITY) { array_push($error, 'toosimple'); } if (mb_strlen($password, $this->encoding) < $this->minimumChars) { array_push($error, 'tooshort'); } // Scale to percentage, so it can be used for a progress bar $complexity = ($complexity / self::$MAX_COMPLEXITY) * 100; $complexity = ($complexity > 100) ? 100 : $complexity; return (object) ['valid' => (is_array($error) || $error instanceof \Countable ? count($error) : 0) === 0, 'complexity' => $complexity, 'errors' => $error]; } /** * Determine the complexity added from a character set if it is used in a string * * @param string $str String to check * @param int [2] $charset Array of unicode code points representing the lower and upper bound of the * character range * * @return int 0 if there are no characters from the character set, size of the character set if there are any * characters used in the string */ private function additionalComplexityForCharset($str, $charset) { $len = mb_strlen($str, $this->encoding); for ($i = 0; $i < $len; $i++) { $c = unpack('Nord', mb_convert_encoding(mb_substr($str, $i, 1, $this->encoding), 'UCS-4BE', $this->encoding)); if ($charset[0] <= $c['ord'] && $c['ord'] <= $charset[1]) { return $charset[1] - $charset[0] + 1; } } return 0; } /** * Check if a string is in the banned password list * * @param string $str String to check * * @return bool TRUE if $str is a banned password, or if it is a substring of a banned password and * $this->banMode is 'strict' */ private function inBanlist($str) { if ($str == '') { return false; } $str = mb_convert_case($str, MB_CASE_LOWER, $this->encoding); if ($this->banMode === 'strict') { for ($i = 0; $i < count($this->bannedPasswords); $i++) { if (mb_strpos($this->bannedPasswords[$i], $str, 0, $this->encoding) !== false) { return true; } } return false; } return in_array($str, $this->bannedPasswords); } }