/** * Formats a password using the current encryption. * * @param string $plaintext The plaintext password to encrypt. * @param string $salt The salt to use to encrypt the password. [] * If not present, a new salt will be * generated. * @param string $encryption The kind of password encryption to use. * Defaults to md5-hex. * @param boolean $show_encrypt Some password systems prepend the kind of * encryption to the crypted password ({SHA}, * etc). Defaults to false. * * @return string The encrypted password. * * @since 11.1 * @note In Joomla! CMS 3.2 the default encrytion has been changed to bcrypt. When PHP 5.5 is the minimum * supported version it will be changed to the PHP PASSWORD_DEFAULT constant. */ public static function getCryptedPassword($plaintext, $salt = '', $encryption = 'bcrypt', $show_encrypt = false) { $app = JFactory::getApplication(); if ($app->getClientId() != 2) { $joomlaPluginEnabled = JPluginHelper::isEnabled('user', 'joomla'); } // The Joomla user plugin allows you to use weaker passwords if necessary. if (!empty($joomlaPluginEnabled)) { JPluginHelper::importPlugin('user', 'joomla'); $userPlugin = JPluginHelper::getPlugin('user', 'joomla'); $userPluginParams = new JRegistry($userPlugin->params); PlgUserJoomla::setDefaultEncryption($userPluginParams); } // Not all controllers check the length, although they should to avoid DOS attacks. // The maximum password length for bcrypt is 55: if (strlen($plaintext) > 55) { $app->enqueueMessage(JText::_('JLIB_USER_ERROR_PASSWORD_TOO_LONG'), 'error'); return false; } // Get the salt to use. if (empty($salt)) { $salt = self::getSalt($encryption, $salt, $plaintext); } // Encrypt the password. switch ($encryption) { case 'plain': return $plaintext; case 'sha': $encrypted = base64_encode(mhash(MHASH_SHA1, $plaintext)); return $show_encrypt ? '{SHA}' . $encrypted : $encrypted; case 'crypt': case 'crypt-des': case 'crypt-md5': case 'crypt-blowfish': return ($show_encrypt ? '{crypt}' : '') . crypt($plaintext, $salt); case 'md5-base64': $encrypted = base64_encode(mhash(MHASH_MD5, $plaintext)); return $show_encrypt ? '{MD5}' . $encrypted : $encrypted; case 'ssha': $encrypted = base64_encode(mhash(MHASH_SHA1, $plaintext . $salt) . $salt); return $show_encrypt ? '{SSHA}' . $encrypted : $encrypted; case 'smd5': $encrypted = base64_encode(mhash(MHASH_MD5, $plaintext . $salt) . $salt); return $show_encrypt ? '{SMD5}' . $encrypted : $encrypted; case 'aprmd5': $length = strlen($plaintext); $context = $plaintext . '$apr1$' . $salt; $binary = self::_bin(md5($plaintext . $salt . $plaintext)); for ($i = $length; $i > 0; $i -= 16) { $context .= substr($binary, 0, $i > 16 ? 16 : $i); } for ($i = $length; $i > 0; $i >>= 1) { $context .= $i & 1 ? chr(0) : $plaintext[0]; } $binary = self::_bin(md5($context)); for ($i = 0; $i < 1000; $i++) { $new = $i & 1 ? $plaintext : substr($binary, 0, 16); if ($i % 3) { $new .= $salt; } if ($i % 7) { $new .= $plaintext; } $new .= $i & 1 ? substr($binary, 0, 16) : $plaintext; $binary = self::_bin(md5($new)); } $p = array(); for ($i = 0; $i < 5; $i++) { $k = $i + 6; $j = $i + 12; if ($j == 16) { $j = 5; } $p[] = self::_toAPRMD5(ord($binary[$i]) << 16 | ord($binary[$k]) << 8 | ord($binary[$j]), 5); } return '$apr1$' . $salt . '$' . implode('', $p) . self::_toAPRMD5(ord($binary[11]), 3); case 'md5-hex': $encrypted = $salt ? md5($plaintext . $salt) : md5($plaintext); return $show_encrypt ? '{MD5}' . $encrypted : $encrypted; case 'sha256': $encrypted = $salt ? hash('sha256', $plaintext . $salt) . ':' . $salt : hash('sha256', $plaintext); return $show_encrypt ? '{SHA256}' . $encrypted : '{SHA256}' . $encrypted; // 'bcrypt' is the default case starting in CMS 3.2. // 'bcrypt' is the default case starting in CMS 3.2. case 'bcrypt': default: $useStrongEncryption = JCrypt::hasStrongPasswordSupport(); if ($useStrongEncryption === true) { $encrypted = password_hash($plaintext, PASSWORD_BCRYPT); if (!$encrypted) { // Something went wrong fall back to sha256. return static::getCryptedPassword($plaintext, '', 'sha256', false); } return $show_encrypt ? '{BCRYPT}' . $encrypted : $encrypted; } else { // BCrypt isn't available but we want strong passwords, fall back to sha256. return static::getCryptedPassword($plaintext, '', 'sha256', false); } } }
/** * Method to bind an associative array of data to a user object * * @param array &$array The associative array to bind to the object * * @return boolean True on success * * @since 11.1 */ public function bind(&$array) { // The Joomla user plugin allows you to use weaker passwords if necessary. $joomlaPluginEnabled = JPluginHelper::isEnabled('user', 'joomla'); if ($joomlaPluginEnabled) { $userPlugin = JPluginHelper::getPlugin('user', 'joomla'); $userPluginParams = new JRegistry($userPlugin->params); JPluginHelper::importPlugin('user', 'joomla'); $defaultEncryption = PlgUserJoomla::setDefaultEncryption($userPluginParams); } else { $defaultEncryption = 'bcrypt'; } // Let's check to see if the user is new or not if (empty($this->id)) { // Check the password and create the crypted password if (empty($array['password'])) { $array['password'] = JUserHelper::genRandomPassword(); $array['password2'] = $array['password']; } // Not all controllers check the password, although they should. // Hence this code is required: if (isset($array['password2']) && $array['password'] != $array['password2']) { JFactory::getApplication()->enqueueMessage(JText::_('JLIB_USER_ERROR_PASSWORD_NOT_MATCH'), 'error'); return false; } $this->password_clear = JArrayHelper::getValue($array, 'password', '', 'string'); $salt = JUserHelper::genRandomPassword(32); $crypt = JUserHelper::getCryptedPassword($array['password'], $salt, $defaultEncryption); $array['password'] = $crypt; // Set the registration timestamp $this->set('registerDate', JFactory::getDate()->toSql()); // Check that username is not greater than 150 characters $username = $this->get('username'); if (strlen($username) > 150) { $username = substr($username, 0, 150); $this->set('username', $username); } // Use a limit to prevent abuse since it is unfiltered // The maximum password length for bcrypt is 55 characters. $password = $this->get('password'); if (strlen($password) > 55) { $password = substr($password, 0, 55); $this->set('password', $password); JFactory::getApplication()->enqueueMessage(JText::_('JLIB_USER_ERROR_PASSWORD_TRUNCATED'), 'notice'); } } else { // Updating an existing user if (!empty($array['password'])) { if ($array['password'] != $array['password2']) { $this->setError(JText::_('JLIB_USER_ERROR_PASSWORD_NOT_MATCH')); return false; } $this->password_clear = JArrayHelper::getValue($array, 'password', '', 'string'); $salt = JUserHelper::genRandomPassword(32); $crypt = JUserHelper::getCryptedPassword($array['password'], $salt, $defaultEncryption); $array['password'] = $crypt . ':' . $salt; } else { $array['password'] = $this->password; } } if (array_key_exists('params', $array)) { $this->_params->loadArray($array['params']); if (is_array($array['params'])) { $params = (string) $this->_params; } else { $params = $array['params']; } $this->params = $params; } // Bind the array if (!$this->setProperties($array)) { $this->setError(JText::_('JLIB_USER_ERROR_BIND_ARRAY')); return false; } // Make sure its an integer $this->id = (int) $this->id; return true; }