/** * Formats a password using the current encryption. If the user ID is given * and the hash does not fit the current hashing algorithm, it automatically * updates the hash. * * @param string $password The plaintext password to check. * @param string $hash The hash to verify against. * @param integer $user_id ID of the user if the password hash should be updated * * @return boolean True if the password and hash match, false otherwise * * @since 3.2.1 */ public static function verifyPassword($password, $hash, $user_id = 0) { $rehash = false; $match = false; // If we are using phpass if (strpos($hash, '$P$') === 0) { // Use PHPass's portable hashes with a cost of 10. $phpass = new PasswordHash(10, true); $match = $phpass->CheckPassword($password, $hash); $rehash = true; } elseif ($hash[0] == '$') { // JCrypt::hasStrongPasswordSupport() includes a fallback for us in the worst case JCrypt::hasStrongPasswordSupport(); $match = password_verify($password, $hash); // Uncomment this line if we actually move to bcrypt. $rehash = password_needs_rehash($hash, PASSWORD_DEFAULT); } elseif (substr($hash, 0, 8) == '{SHA256}') { // Check the password $parts = explode(':', $hash); $crypt = $parts[0]; $salt = @$parts[1]; $testcrypt = static::getCryptedPassword($password, $salt, 'sha256', true); $match = JCrypt::timingSafeCompare($hash, $testcrypt); $rehash = true; } else { // Check the password $parts = explode(':', $hash); $crypt = $parts[0]; $salt = @$parts[1]; $rehash = true; // Compile the hash to compare // If the salt is empty AND there is a ':' in the original hash, we must append ':' at the end $testcrypt = md5($password . $salt) . ($salt ? ':' . $salt : (strpos($hash, ':') !== false ? ':' : '')); $match = JCrypt::timingSafeCompare($hash, $testcrypt); } // If we have a match and rehash = true, rehash the password with the current algorithm. if ((int) $user_id > 0 && $match && $rehash) { $user = new JUser($user_id); $user->password = static::hashPassword($password); $user->save(); } return $match; }
/** * Verifies a password hash * * @param string $password The password to verify. * @param string $hash The password hash to check. * * @return boolean True if the password is valid, false otherwise. * * @since 12.2 * @deprecated 4.0 Use PHP 5.5's native password hashing API */ public function verify($password, $hash) { // Check if the hash is a blowfish hash. if (substr($hash, 0, 4) == '$2a$' || substr($hash, 0, 4) == '$2y$') { $type = '$2a$'; if (JCrypt::hasStrongPasswordSupport()) { $type = '$2y$'; } return password_verify($password, $hash); } // Check if the hash is an MD5 hash. if (substr($hash, 0, 3) == '$1$') { return JCrypt::timingSafeCompare(crypt($password, $hash), $hash); } // Check if the hash is a Joomla hash. if (preg_match('#[a-z0-9]{32}:[A-Za-z0-9]{32}#', $hash) === 1) { // Check the password $parts = explode(':', $hash); $salt = @$parts[1]; // Compile the hash to compare // If the salt is empty AND there is a ':' in the original hash, we must append ':' at the end $testcrypt = md5($password . $salt) . ($salt ? ':' . $salt : (strpos($hash, ':') !== false ? ':' : '')); return JCrypt::timingSafeCompare($hash, $testcrypt); } return false; }
function plgSystemImproved_Ajax_Login(&$subject, $config) { parent::__construct($subject, $config); $GLOBALS['username=email'] = $this->params->get('generate', 1) < 1; if (isset($_REQUEST['ialCheck'])) { $check = JRequest::getString('ialCheck'); $json = array('error' => '', 'msg' => ''); switch ($check) { case 'ialLogin': $json['field'] = 'password'; if (JSession::checkToken()) { $user = JRequest::getVar(isset($_REQUEST['username']) ? 'username' : 'email', ''); $password = JRequest::getString('password', '', 'method', JREQUEST_ALLOWRAW); if (!empty($password)) { $result = isset($_REQUEST['username']) ? OUserHelper::getUser($user) : OUserHelper::getUserByEmail($user); if ($result) { $match = 0; if (method_exists('JUserHelper', 'verifyPassword')) { $match = JUserHelper::verifyPassword($password, $result->password, $result->id); } elseif (substr($result->password, 0, 4) == '$2y$') { $password60 = substr($result->password, 0, 60); if (JCrypt::hasStrongPasswordSupport()) { $match = password_verify($password, $password60); } } else { $parts = explode(':', $result->password); $crypt = $parts[0]; $salt = @$parts[1]; $cryptmode = substr($result->password, 0, 8) == '{SHA256}' ? 'sha256' : 'md5-hex'; $testcrypt = JUserHelper::getCryptedPassword($password, $salt, $cryptmode, false); $match = $crypt == $testcrypt || $result->password == $testcrypt; } if ($match) { $json['username'] = $result->username; } else { $json['error'] = 'JGLOBAL_AUTH_INVALID_PASS'; } } else { $json['error'] = 'JGLOBAL_AUTH_NO_USER'; } } else { $json['error'] = 'JGLOBAL_AUTH_EMPTY_PASS_NOT_ALLOWED'; } } else { $json['error'] = 'JINVALID_TOKEN'; } $json['msg'] = JText::_($json['error']); die(json_encode($json)); case 'data[register][username]': case 'jform[username]': case 'username': $username = JRequest::getString('value'); if (OUserHelper::getId($username)) { $json['error'] = 'COM_USERS_REGISTER_USERNAME_MESSAGE'; } $json['msg'] = JText::_($json['error']); die(json_encode($json)); case 'data[register][email]': case 'jform[email1]': case 'email': $email = JRequest::getString('value'); if (OUserHelper::getIdByEmail($email)) { $json['error'] = 'COM_USERS_REGISTER_EMAIL1_MESSAGE'; } $json['msg'] = JText::_($json['error']); die(json_encode($json)); case 'ialRegister': // com_users if ($jf = JRequest::getVar('jform', null, 'array')) { if (!JSession::checkToken()) { $json['error'] = 'JINVALID_TOKEN'; $json['msg'] = JText::_($json['error']); die(json_encode($json)); } if (!isset($jf['email1'])) { $json['error'] = 'JGLOBAL_EMAIL'; $json['msg'] = JText::_('JGLOBAL_EMAIL') . ' ' . JText::_('JREQUIRED'); die(json_encode($json)); } if (!isset($jf['password1'])) { $json['error'] = 'JGLOBAL_PASSWORD'; $json['msg'] = JText::_('JGLOBAL_PASSWORD') . ' ' . JText::_('JREQUIRED'); die(json_encode($json)); } if (!isset($jf['username'])) { if ($this->params->get('generate', 1) > 0) { list($jf['username']) = explode('@', $jf['email1']); if (OUserHelper::getId($jf['username'])) { $jf['username'] .= OUserHelper::getNewId(); } } else { $jf['username'] = $jf['email1']; } } if (!isset($jf['name'])) { $jf['name'] = $jf['username']; } if (!isset($jf['email2'])) { $jf['email2'] = $jf['email1']; } if (!isset($jf['password2'])) { $jf['password2'] = $jf['password1']; } JRequest::setVar('jform', $jf); JFactory::getApplication()->input->post->set('jform', $jf); } $_SESSION['ialRegister'] = $jf['username']; break; } } }
/** * This method should handle any authentication and report back to the subject * * @param array $credentials Array holding the user credentials * @param array $options Array of extra options * @param object &$response Authentication response object * * @return boolean * * @since 1.5 */ public function onUserAuthenticate($credentials, $options, &$response) { $response->type = 'Joomla'; // Joomla does not like blank passwords if (empty($credentials['password'])) { $response->status = JAuthentication::STATUS_FAILURE; $response->error_message = JText::_('JGLOBAL_AUTH_EMPTY_PASS_NOT_ALLOWED'); return false; } // Get a database object $db = JFactory::getDbo(); $query = $db->getQuery(true)->select('id, password')->from('#__users')->where('username='******'username'])); $db->setQuery($query); $result = $db->loadObject(); if ($result) { if (substr($result->password, 0, 4) == '$2y$') { // BCrypt passwords are always 60 characters, but it is possible that salt is appended although non standard. $password60 = substr($result->password, 0, 60); if (JCrypt::hasStrongPasswordSupport()) { $match = password_verify($credentials['password'], $password60); } } elseif (substr($result->password, 0, 8) == '{SHA256}') { // Check the password $parts = explode(':', $result->password); $crypt = $parts[0]; $salt = @$parts[1]; $testcrypt = JUserHelper::getCryptedPassword($credentials['password'], $salt, 'sha256', false); if ($result->password == $testcrypt) { $match = true; } } else { // Check the password $parts = explode(':', $result->password); $crypt = $parts[0]; $salt = @$parts[1]; $testcrypt = JUserHelper::getCryptedPassword($credentials['password'], $salt, 'md5-hex', false); if ($crypt == $testcrypt) { $match = true; } } if (isset($match) && $match === true) { // Bring this in line with the rest of the system $user = JUser::getInstance($result->id); $response->email = $user->email; $response->fullname = $user->name; if (JFactory::getApplication()->isAdmin()) { $response->language = $user->getParam('admin_language'); } else { $response->language = $user->getParam('language'); } $response->status = JAuthentication::STATUS_SUCCESS; $response->error_message = ''; } else { // Invalid password $response->status = JAuthentication::STATUS_FAILURE; $response->error_message = JText::_('JGLOBAL_AUTH_INVALID_PASS'); } } else { // Invalid user $response->status = JAuthentication::STATUS_FAILURE; $response->error_message = JText::_('JGLOBAL_AUTH_NO_USER'); } // Check the two factor authentication if ($response->status == JAuthentication::STATUS_SUCCESS) { require_once JPATH_ADMINISTRATOR . '/components/com_users/helpers/users.php'; $methods = UsersHelper::getTwoFactorMethods(); if (count($methods) <= 1) { // No two factor authentication method is enabled return; } require_once JPATH_ADMINISTRATOR . '/components/com_users/models/user.php'; $model = new UsersModelUser(); // Load the user's OTP (one time password, a.k.a. two factor auth) configuration if (!array_key_exists('otp_config', $options)) { $otpConfig = $model->getOtpConfig($result->id); $options['otp_config'] = $otpConfig; } else { $otpConfig = $options['otp_config']; } // Check if the user has enabled two factor authentication if (empty($otpConfig->method) || $otpConfig->method == 'none') { // Warn the user if he's using a secret code but he has not // enabed two factor auth in his account. if (!empty($credentials['secretkey'])) { try { $app = JFactory::getApplication(); $this->loadLanguage(); $app->enqueueMessage(JText::_('PLG_AUTH_JOOMLA_ERR_SECRET_CODE_WITHOUT_TFA'), 'warning'); } catch (Exception $exc) { // This happens when we are in CLI mode. In this case // no warning is issued return; } } return; } // Load the Joomla! RAD layer if (!defined('FOF_INCLUDED')) { include_once JPATH_LIBRARIES . '/fof/include.php'; } // Try to validate the OTP FOFPlatform::getInstance()->importPlugin('twofactorauth'); $otpAuthReplies = FOFPlatform::getInstance()->runPlugins('onUserTwofactorAuthenticate', array($credentials, $options)); $check = false; /* * This looks like noob code but DO NOT TOUCH IT and do not convert * to in_array(). During testing in_array() inexplicably returned * null when the OTEP begins with a zero! o_O */ if (!empty($otpAuthReplies)) { foreach ($otpAuthReplies as $authReply) { $check = $check || $authReply; } } // Fall back to one time emergency passwords if (!$check) { // Did the user use an OTEP instead? if (empty($otpConfig->otep)) { if (empty($otpConfig->method) || $otpConfig->method == 'none') { // Two factor authentication is not enabled on this account. // Any string is assumed to be a valid OTEP. return true; } else { /* * Two factor authentication enabled and no OTEPs defined. The * user has used them all up. Therefore anything he enters is * an invalid OTEP. */ return false; } } // Clean up the OTEP (remove dashes, spaces and other funny stuff // our beloved users may have unwittingly stuffed in it) $otep = $credentials['secretkey']; $otep = filter_var($otep, FILTER_SANITIZE_NUMBER_INT); $otep = str_replace('-', '', $otep); $check = false; // Did we find a valid OTEP? if (in_array($otep, $otpConfig->otep)) { // Remove the OTEP from the array $otpConfig->otep = array_diff($otpConfig->otep, array($otep)); $model->setOtpConfig($result->id, $otpConfig); // Return true; the OTEP was a valid one $check = true; } } if (!$check) { $response->status = JAuthentication::STATUS_FAILURE; $response->error_message = JText::_('JGLOBAL_AUTH_INVALID_SECRETKEY'); } } }
/** * Verifies a password hash * * @param string $password The password to verify. * @param string $hash The password hash to check. * * @return boolean True if the password is valid, false otherwise. * * @since 12.2 * @deprecated 4.0 Use PHP 5.5's native password hashing API */ public function verify($password, $hash) { // Check if the hash is a blowfish hash. if (substr($hash, 0, 4) == '$2a$' || substr($hash, 0, 4) == '$2y$') { $type = '$2a$'; if (JCrypt::hasStrongPasswordSupport()) { $type = '$2y$'; } $hash = $type . substr($hash, 4); return crypt($password, $hash) === $hash; } // Check if the hash is an MD5 hash. if (substr($hash, 0, 3) == '$1$') { return crypt($password, $hash) === $hash; } // Check if the hash is a Joomla hash. if (preg_match('#[a-z0-9]{32}:[A-Za-z0-9]{32}#', $hash) === 1) { return md5($password . substr($hash, 33)) === substr($hash, 0, 32); } return false; }
/** * Method to unlock a password protected category * * @param int $catid ID of the category to unlock * @param string $password Password of the category to check * @return boolean True on success, false otherwise * @since 3.1 */ public function unlock($catid, $password) { $query = $this->_db->getQuery(true)->select('cid, password')->from($this->_db->quoteName(_JOOM_TABLE_CATEGORIES))->where('cid = ' . (int) $catid); $this->_db->setQuery($query); if (!($category = $this->_db->loadObject())) { throw new Exception($this->_db->getErrorMsg()); } if (!$category->password) { throw new Exception('Category is not protected.'); } $match = false; if (substr($category->password, 0, 4) == '$2y$') { // BCrypt passwords are always 60 characters, but it is possible that salt is appended although non standard. $password60 = substr($category->password, 0, 60); if (JCrypt::hasStrongPasswordSupport()) { $match = password_verify($password, $password60); } } else { if (substr($category->password, 0, 8) == '{SHA256}') { // Check the password $parts = explode(':', $category->password); $crypt = $parts[0]; $salt = @$parts[1]; $testcrypt = JUserHelper::getCryptedPassword($password, $salt, 'sha256', false); if ($category->password == $testcrypt) { $match = true; } } else { // Check the password $parts = explode(':', $category->password); $crypt = $parts[0]; $salt = @$parts[1]; $testcrypt = JUserHelper::getCryptedPassword($password, $salt, 'md5-hex', false); if ($crypt == $testcrypt) { $match = true; } } } if (!$match) { throw new Exception(JText::_('COM_JOOMGALLERY_CATEGORY_WRONG_PASSWORD')); } $categories = $this->_mainframe->getUserState('joom.unlockedCategories', array(0)); $categories = array_unique(array_merge($categories, array($catid))); $this->_mainframe->setUserState('joom.unlockedCategories', $categories); return true; }
/** * Method to create the root user for the site * * @param object $options The session options * * @return boolean True on success * * @since 3.1 */ private function _createRootUser($options) { // Get the application /* @var InstallationApplicationWeb $app */ $app = JFactory::getApplication(); // Get a database object. try { $db = InstallationHelperDatabase::getDBO($options->db_type, $options->db_host, $options->db_user, $options->db_pass, $options->db_name, $options->db_prefix); } catch (RuntimeException $e) { $app->enqueueMessage(JText::sprintf('INSTL_ERROR_CONNECT_DB', $e->getMessage()), 'notice'); return false; } $useStrongPasswords = JCrypt::hasStrongPasswordSupport(); if ($useStrongPasswords) { $cryptpass = JUserHelper::getCryptedPassword($options->admin_password); } else { $salt = JUserHelper::genRandomPassword(16); //$cryptpass = JUserHelper::getCryptedPassword($options->admin_password, $salt, 'sha256') . ':' . $salt; $cryptpass = JUserHelper::getCryptedPassword($options->admin_password, $salt, 'sha256'); } // Take the admin user id $userId = InstallationModelDatabase::getUserId(); // We don't need the randUserId in the session any longer, let's remove it InstallationModelDatabase::resetRandUserId(); // Create the admin user date_default_timezone_set('UTC'); $installdate = date('Y-m-d H:i:s'); $nullDate = $db->getNullDate(); // Sqlsrv change $query = $db->getQuery(true)->select($db->quoteName('id'))->from($db->quoteName('#__users'))->where($db->quoteName('id') . ' = ' . $db->quote($userId)); $db->setQuery($query); if ($db->loadResult()) { $query->clear()->update($db->quoteName('#__users'))->set($db->quoteName('name') . ' = ' . $db->quote('Super User'))->set($db->quoteName('username') . ' = ' . $db->quote($options->admin_user))->set($db->quoteName('email') . ' = ' . $db->quote($options->admin_email))->set($db->quoteName('password') . ' = ' . $db->quote($cryptpass))->set($db->quoteName('block') . ' = 0')->set($db->quoteName('sendEmail') . ' = 1')->set($db->quoteName('registerDate') . ' = ' . $db->quote($installdate))->set($db->quoteName('lastvisitDate') . ' = ' . $db->quote($nullDate))->set($db->quoteName('activation') . ' = ' . $db->quote('0'))->set($db->quoteName('params') . ' = ' . $db->quote(''))->where($db->quoteName('id') . ' = ' . $db->quote($userId)); } else { $columns = array($db->quoteName('id'), $db->quoteName('name'), $db->quoteName('username'), $db->quoteName('email'), $db->quoteName('password'), $db->quoteName('block'), $db->quoteName('sendEmail'), $db->quoteName('registerDate'), $db->quoteName('lastvisitDate'), $db->quoteName('activation'), $db->quoteName('params')); $query->clear()->insert('#__users', true)->columns($columns)->values($db->quote($userId) . ', ' . $db->quote('Super User') . ', ' . $db->quote($options->admin_user) . ', ' . $db->quote($options->admin_email) . ', ' . $db->quote($cryptpass) . ', ' . $db->quote('0') . ', ' . $db->quote('1') . ', ' . $db->quote($installdate) . ', ' . $db->quote($nullDate) . ', ' . $db->quote('0') . ', ' . $db->quote('')); } $db->setQuery($query); try { $db->execute(); } catch (RuntimeException $e) { $app->enqueueMessage($e->getMessage(), 'notice'); return false; } // Map the super admin to the Super Admin Group $query->clear()->select($db->quoteName('user_id'))->from($db->quoteName('#__user_usergroup_map'))->where($db->quoteName('user_id') . ' = ' . $db->quote($userId)); $db->setQuery($query); if ($db->loadResult()) { $query->clear()->update($db->quoteName('#__user_usergroup_map'))->set($db->quoteName('user_id') . ' = ' . $db->quote($userId))->set($db->quoteName('group_id') . ' = 8'); } else { $query->clear()->insert($db->quoteName('#__user_usergroup_map'), false)->columns(array($db->quoteName('user_id'), $db->quoteName('group_id')))->values($db->quote($userId) . ', 8'); } $db->setQuery($query); try { $db->execute(); } catch (RuntimeException $e) { $app->enqueueMessage($e->getMessage(), 'notice'); return false; } return true; }
/** * This method should handle any authentication and report back to the subject * * @param array $credentials Array holding the user credentials * @param array $options Array of extra options * @param object &$response Authentication response object * * @return boolean * * @since 3.2 */ public function onUserAuthenticate($credentials, $options, &$response) { // No remember me for admin if ($this->app->isAdmin()) { return false; } JLoader::register('JAuthentication', JPATH_LIBRARIES . '/joomla/user/authentication.php'); $response->type = 'Cookie'; // We need to validate the cookie data because there may be no Remember Me plugin to do it. // Create the cookie name and data. $rememberArray = JUserHelper::getRememberCookieData(); if ($rememberArray == false) { return false; } list($privateKey, $series, $uastring) = $rememberArray; // Find the matching record if it exists. $query = $this->db->getQuery(true)->select($this->db->quoteName(array('user_id', 'token', 'series', 'time', 'invalid')))->from($this->db->quoteName('#__user_keys'))->where($this->db->quoteName('series') . ' = ' . $this->db->quote(base64_encode($series)))->where($this->db->quoteName('uastring') . ' = ' . $this->db->quote($uastring))->order($this->db->quoteName('time') . ' DESC'); $results = $this->db->setQuery($query)->loadObjectList(); $countResults = count($results); if ($countResults !== 1) { $response->status = JAuthentication::STATUS_FAILURE; return; } else { if (substr($results[0]->token, 0, 4) === '$2y$') { if (JCrypt::hasStrongPasswordSupport()) { $match = password_verify($privateKey, $results[0]->token); } } else { if (JCrypt::timingSafeCompare($results[0]->token, $privateKey)) { $match = true; } } if (empty($match)) { JUserHelper::invalidateCookie($results[0]->user_id, $uastring); JLog::add(JText::sprintf('PLG_SYSTEM_REMEMBER_ERROR_LOG_LOGIN_FAILED', $user->username), JLog::WARNING, 'security'); $response->status = JAuthentication::STATUS_FAILURE; return false; } } // Set cookie params. if (!empty($options['lifetime']) && !empty($options['length']) && !empty($options['secure'])) { $response->lifetime = $options['lifetime']; $response->length = $options['length']; $response->secure = $options['secure']; } // Make sure there really is a user with this name and get the data for the session. $query = $this->db->getQuery(true)->select($this->db->quoteName(array('id', 'username', 'password')))->from($this->db->quoteName('#__users'))->where($this->db->quoteName('username') . ' = ' . $this->db->quote($credentials['username'])); $result = $this->db->setQuery($query)->loadObject(); if ($result) { // Bring this in line with the rest of the system $user = JUser::getInstance($result->id); $cookieName = JUserHelper::getShortHashedUserAgent(); // If there is no cookie, bail out if (!$this->app->input->cookie->get($cookieName)) { return; } // Set response data. $response->username = $result->username; $response->email = $user->email; $response->fullname = $user->name; $response->password = $result->password; $response->language = $user->getParam('language'); // Set response status. $response->status = JAuthentication::STATUS_SUCCESS; $response->error_message = ''; } else { $response->status = JAuthentication::STATUS_FAILURE; $response->error_message = JText::_('JGLOBAL_AUTH_NO_USER'); } }
/** * Remember me method to run onAfterInitialise * * @return boolean * * @since 1.5 * @throws InvalidArgumentException */ public function onAfterInitialise() { // No remember me for admin if ($this->app->isAdmin()) { return false; } $user = JFactory::getUser(); $this->app->rememberCookieLifetime = $this->lifetime; $this->app->rememberCookieSecure = $this->secure; $this->app->rememberCookieLength = $this->length; // Check for a cookie if ($user->get('guest') == 1) { // Create the cookie name and data $rememberArray = JUserHelper::getRememberCookieData(); if ($rememberArray !== false) { if (count($rememberArray) != 3) { // Destroy the cookie in the browser. $this->app->input->cookie->set(end($rememberArray), false, time() - 42000, $this->app->get('cookie_path'), $this->app->get('cookie_domain')); JLog::add('Invalid cookie detected.', JLog::WARNING, 'error'); return false; } list($privateKey, $series, $uastring) = $rememberArray; if (!JUserHelper::clearExpiredTokens($this)) { JLog::add('Error in deleting expired cookie tokens.', JLog::WARNING, 'error'); } // Find the matching record if it exists $query = $this->db->getQuery(true)->select($this->db->quoteName(array('user_id', 'token', 'series', 'time', 'invalid')))->from($this->db->quoteName('#__user_keys'))->where($this->db->quoteName('series') . ' = ' . $this->db->quote(base64_encode($series)))->where($this->db->quoteName('uastring') . ' = ' . $this->db->quote($uastring))->order($this->db->quoteName('time') . ' DESC'); $results = $this->db->setQuery($query)->loadObjectList(); $countResults = count($results); // We have a user but a cookie that is not in the database, or it is invalid. This is a possible attack, so invalidate everything. if (($countResults === 0 || $results[0]->invalid != 0) && !empty($results[0]->user_id)) { JUserHelper::invalidateCookie($results[0]->user_id, $uastring); JLog::add(JText::sprintf('PLG_SYSTEM_REMEMBER_ERROR_LOG_INVALIDATED_COOKIES', $user->username), JLog::WARNING, 'security'); // Possibly e-mail user and admin here. return false; } // We have a user with one cookie with a valid series and a corresponding record in the database. if ($countResults === 1) { if (substr($results[0]->token, 0, 4) === '$2y$') { if (JCrypt::hasStrongPasswordSupport()) { $match = password_verify($privateKey, $results[0]->token); } } else { if (JCrypt::timingSafeCompare($results[0]->token, $privateKey)) { $match = true; } } if (empty($match)) { JUserHelper::invalidateCookie($results[0]->user_id, $uastring); JLog::add(JText::sprintf('PLG_SYSTEM_REMEMBER_ERROR_LOG_LOGIN_FAILED', $user->username), JLog::WARNING, 'security'); return false; } // Set up the credentials array to pass to onUserAuthenticate $credentials = array('username' => $results[0]->user_id); return $this->app->login($credentials, array('silent' => true, 'lifetime' => $this->lifetime, 'secure' => $this->secure, 'length' => $this->length)); } } } return false; }
/** * 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); } } }
public function verifyUserPassword($userid, $password) { $db = Jfactory::getDbo(); $query = $db->getQuery(true)->select('password')->from('#__users')->where('id=' . $db->quote($userid)); $db->setQuery($query); $result = $db->loadResult(); $match = false; if (!empty($result)) { if (strpos($result, '$P$') === 0) { $phpass = new PasswordHash(10, true); $match = $phpass->CheckPassword($password, $result); } elseif (substr($result, 0, 4) == '$2y$') { $password60 = substr($result, 0, 60); if (JCrypt::hasStrongPasswordSupport()) { $match = password_verify($password, $password60); } } elseif (substr($result, 0, 8) == '{SHA256}') { $parts = explode(':', $result); $crypt = $parts[0]; $salt = @$parts[1]; $testcrypt = JUserHelper::getCryptedPassword($password, $salt, 'sha256', false); $match = $result == $testcrypt; } else { $parts = explode(':', $result); $crypt = $parts[0]; $salt = @$parts[1]; $testcrypt = JUserHelper::getCryptedPassword($password, $salt, 'md5-hex', false); $match = $crypt == $testcrypt; } } return $match; }
/** * This method should handle any login logic and report back to the subject * * @param array $user Holds the user data * @param array $options Array holding options (remember, autoregister, group) * * @return boolean True on success * * @since 1.5 */ public function onUserLogin($user, $options = array()) { $instance = $this->_getUser($user, $options); // If _getUser returned an error, then pass it back. if ($instance instanceof Exception) { return false; } // If the user is blocked, redirect with an error if ($instance->get('block') == 1) { $this->app->enqueueMessage(JText::_('JERROR_NOLOGIN_BLOCKED'), 'warning'); return false; } // Authorise the user based on the group information if (!isset($options['group'])) { $options['group'] = 'USERS'; } // Check the user can login. $result = $instance->authorise($options['action']); if (!$result) { $this->app->enqueueMessage(JText::_('JERROR_LOGIN_DENIED'), 'warning'); return false; } // Mark the user as logged in $instance->set('guest', 0); // If the user has an outdated hash, update it. if (substr($user['password'], 0, 4) != '$2y$' && $this->useStrongEncryption && JCrypt::hasStrongPasswordSupport() == true) { if (strlen($user['password']) > 55) { $user['password'] = substr($user['password'], 0, 55); JFactory::getApplication()->enqueueMessage(JText::_('JLIB_USER_ERROR_PASSWORD_TRUNCATED'), 'notice'); } $instance->password = password_hash($user['password'], PASSWORD_BCRYPT); $instance->save(); } // Register the needed session variables $session = JFactory::getSession(); $session->set('user', $instance); // Check to see the the session already exists. $this->app->checkSession(); // Update the user related fields for the Joomla sessions table. $query = $this->db->getQuery(true)->update($this->db->quoteName('#__session'))->set($this->db->quoteName('guest') . ' = ' . $this->db->quote($instance->guest))->set($this->db->quoteName('username') . ' = ' . $this->db->quote($instance->username))->set($this->db->quoteName('userid') . ' = ' . (int) $instance->id)->where($this->db->quoteName('session_id') . ' = ' . $this->db->quote($session->getId())); $this->db->setQuery($query)->execute(); // Hit the user last visit field $instance->setLastVisit(); return true; }