/** * Serialize and save data to memcache * * Note that it also sets a time to live for the * cached version set to self::TTL * * @param string $cacheKey Cache key * @param mixed $data Data to send to memcached, will use serialize(); * * @return null */ private function memcacheSave($data) { $url_friendly_key = MWCryptRand::generateHex(16); $key = wfMemcKey($url_friendly_key, 'wpdsso'); $this->memcache->set($key, json_encode($data), self::TTL); return $url_friendly_key; }
public function execute() { $user = $this->getUser(); $params = $this->extractRequestParams(); // If we're in JSON callback mode, no tokens can be obtained if ($this->lacksSameOriginSecurity()) { $this->dieUsage('Cannot obtain a centralauthtoken when using a callback', 'hascallback'); } if ($user->isAnon()) { $this->dieUsage('Anonymous users cannot obtain a centralauthtoken', 'notloggedin'); } if (CentralAuthHooks::hasApiToken()) { $this->dieUsage('Cannot obtain a centralauthtoken when using centralauthtoken', 'norecursion'); } $centralUser = CentralAuthUser::getInstance($user); if (!$centralUser->exists() || !$centralUser->isAttached()) { $this->dieUsage('Cannot obtain a centralauthtoken without an attached global account', 'notattached'); } $data = array('userName' => $user->getName(), 'token' => $centralUser->getAuthToken()); global $wgMemc; $loginToken = MWCryptRand::generateHex(32) . dechex($centralUser->getId()); $key = CentralAuthUser::memcKey('api-token', $loginToken); $wgMemc->add($key, $data, 60); $this->getResult()->addValue(null, $this->getModuleName(), array('centralauthtoken' => $loginToken)); }
public function crypt($password) { if (count($this->args) == 0) { $this->args[] = base64_encode(MWCryptRand::generate(16, true)); } if (function_exists('hash_pbkdf2')) { $hash = hash_pbkdf2($this->params['algo'], $password, base64_decode($this->args[0]), (int) $this->params['rounds'], (int) $this->params['length'], true); if (!is_string($hash)) { throw new PasswordError('Error when hashing password.'); } } else { $hashLenHash = hash($this->params['algo'], '', true); if (!is_string($hashLenHash)) { throw new PasswordError('Error when hashing password.'); } $hashLen = strlen($hashLenHash); $blockCount = ceil($this->params['length'] / $hashLen); $hash = ''; $salt = base64_decode($this->args[0]); for ($i = 1; $i <= $blockCount; ++$i) { $roundTotal = $lastRound = hash_hmac($this->params['algo'], $salt . pack('N', $i), $password, true); for ($j = 1; $j < $this->params['rounds']; ++$j) { $lastRound = hash_hmac($this->params['algo'], $lastRound, $password, true); $roundTotal ^= $lastRound; } $hash .= $roundTotal; } $hash = substr($hash, 0, $this->params['length']); } $this->hash = base64_encode($hash); }
static function lqtThread($parser, $args, $parser, $frame) { $pout = $parser->getOutput(); // Prepare information. $title = Title::newFromText($args['thread']); $thread = null; if ($args['thread']) { if (is_numeric($args['thread'])) { $thread = Threads::withId($args['thread']); } elseif ($title) { $article = new Article($title, 0); $thread = Threads::withRoot($article); } } if (is_null($thread)) { return ''; } $data = array('type' => 'thread', 'args' => $args, 'thread' => $thread->id(), 'title' => $thread->title()); if (!isset($pout->mLqtReplacements)) { $pout->mLqtReplacements = array(); } // Generate a token $tok = MWCryptRand::generateHex(32); $text = '<!--LQT-THREAD-' . $tok . '-->'; $pout->mLqtReplacements[$text] = $data; return $text; }
public function crypt($plaintext) { if (count($this->args) == 0) { $this->args[] = MWCryptRand::generateHex(8); } $this->hash = md5($this->args[0] . '-' . md5($plaintext)); }
public function crypt($plaintext) { if (count($this->args) == 0) { $this->args[] = MWCryptRand::generateHex(8); } $this->hash = md5($this->args[0] . '-' . md5($plaintext)); if (!is_string($this->hash) || strlen($this->hash) < 32) { throw new PasswordError('Error when hashing password.'); } }
protected function setUp() { // Needs to be before setup since this gets cached $this->mergeMwGlobalArrayValue('wgGroupPermissions', array('sysop' => array('deleterevision' => true))); parent::setUp(); // Make a few edits for us to play with for ($i = 1; $i <= 5; $i++) { self::editPage(self::$page, MWCryptRand::generateHex(10), 'summary'); $this->revs[] = Title::newFromText(self::$page)->getLatestRevID(Title::GAID_FOR_UPDATE); } }
protected static function getPreloadId($create_if_not_exists) { global $wgUser, $wgRequest; if (!$wgUser->isAnon()) { return ModerationPreload::User_to_PreloadId($wgUser); } $anon_id = $wgRequest->getSessionData('anon_id'); if (!$anon_id) { if (!$create_if_not_exists) { return false; } $anon_id = MWCryptRand::generateHex(32); $wgRequest->setSessionData('anon_id', $anon_id); } return ModerationPreload::AnonId_to_PreloadId($anon_id); }
/** * Updates the underlying hash by encrypting it with the newest secret. * * @throws MWException If the configuration is not valid * @return bool True if the password was updated */ public function update() { if (count($this->args) != 2 || $this->params == $this->getDefaultParams()) { // Hash does not need updating return false; } // Decrypt the underlying hash $underlyingHash = openssl_decrypt(base64_decode($this->args[1]), $this->params['cipher'], $this->config['secrets'][$this->params['secret']], 0, base64_decode($this->args[0])); // Reset the params $this->params = $this->getDefaultParams(); // Check the key size with the new params $iv = MWCryptRand::generate(openssl_cipher_iv_length($this->params['cipher']), true); $this->hash = base64_encode(openssl_encrypt($underlyingHash, $this->params['cipher'], $this->config['secrets'][$this->params['secret']], 0, $iv)); $this->args = array(base64_encode($iv)); return true; }
/** * @param string $password Password to encrypt * * @throws PasswordError If bcrypt has an unknown error * @throws MWException If bcrypt is not supported by PHP */ public function crypt($password) { if (!defined('CRYPT_BLOWFISH')) { throw new MWException('Bcrypt is not supported.'); } // Either use existing hash or make a new salt // Bcrypt expects 22 characters of base64-encoded salt // Note: bcrypt does not use MIME base64. It uses its own base64 without any '=' padding. // It expects a 128 bit salt, so it will ignore anything after the first 128 bits if (!isset($this->args[0])) { $this->args[] = substr(strtr(base64_encode(MWCryptRand::generate(16, true)), '+', '.'), 0, 22); } $hash = crypt($password, sprintf('$2y$%02d$%s', (int) $this->params['rounds'], $this->args[0])); if (!is_string($hash) || strlen($hash) <= 13) { throw new PasswordError('Error when hashing password.'); } // Strip the $2y$ $parts = explode($this->getDelimiter(), substr($hash, 4)); $this->params['rounds'] = (int) $parts[0]; $this->args[0] = substr($parts[1], 0, 22); $this->hash = substr($parts[1], 22); }
/** * Set a value in the session, encrypted * * This relies on the secrecy of $wgSecretKey (by default), or $wgSessionSecret. * * @param string|int $key * @param mixed $value */ public function setSecret($key, $value) { global $wgSessionInsecureSecrets; list($encKey, $hmacKey) = $this->getSecretKeys(); $serialized = serialize($value); // The code for encryption (with OpenSSL) and sealing is taken from // Chris Steipp's OATHAuthUtils class in Extension::OATHAuth. // Encrypt // @todo: import a pure-PHP library for AES instead of doing $wgSessionInsecureSecrets $iv = \MWCryptRand::generate(16, true); if (function_exists('openssl_encrypt')) { $ciphertext = openssl_encrypt($serialized, 'aes-256-ctr', $encKey, OPENSSL_RAW_DATA, $iv); if ($ciphertext === false) { throw new UnexpectedValueException('Encryption failed: ' . openssl_error_string()); } } elseif (function_exists('mcrypt_encrypt')) { $ciphertext = mcrypt_encrypt('rijndael-128', $encKey, $serialized, 'ctr', $iv); if ($ciphertext === false) { throw new UnexpectedValueException('Encryption failed'); } } elseif ($wgSessionInsecureSecrets) { $ex = new \Exception('No encryption is available, storing data as plain text'); $this->logger->warning($ex->getMessage(), ['exception' => $ex]); $ciphertext = $serialized; } else { throw new \BadMethodCallException('Encryption is not available. You really should install the PHP OpenSSL extension, ' . 'or failing that the mcrypt extension. But if you really can\'t and you\'re willing ' . 'to accept insecure storage of sensitive session data, set ' . '$wgSessionInsecureSecrets = true in LocalSettings.php to make this exception go away.'); } // Seal $sealed = base64_encode($iv) . '.' . base64_encode($ciphertext); $hmac = hash_hmac('sha256', $sealed, $hmacKey, true); $encrypted = base64_encode($hmac) . '.' . $sealed; // Store $this->set($key, $encrypted); }
/** * Generate, store, and return a new e-mail confirmation code. * A hash (unsalted, since it's used as a key) is stored. * * @note Call saveSettings() after calling this function to commit * this change to the database. * * @param string &$expiration Accepts the expiration time * @return string New token */ protected function confirmationToken(&$expiration) { global $wgUserEmailConfirmationTokenExpiry; $now = time(); $expires = $now + $wgUserEmailConfirmationTokenExpiry; $expiration = wfTimestamp(TS_MW, $expires); $this->load(); $token = MWCryptRand::generateHex(32); $hash = md5($token); $this->mEmailToken = $hash; $this->mEmailTokenExpires = $expiration; return $token; }
/** * Return a singleton instance of MWCryptRand * @return MWCryptRand */ protected static function singleton() { if (is_null(self::$singleton)) { self::$singleton = new self(); } return self::$singleton; }
/** * Return an RFC4122 compliant v4 UUID * * @param int $flags Bitfield (supports UIDGenerator::QUICK_RAND) * @return string * @throws MWException */ public static function newUUIDv4($flags = 0) { $hex = $flags & self::QUICK_RAND ? wfRandomString(31) : MWCryptRand::generateHex(31); return sprintf('%s-%s-%s-%s-%s', substr($hex, 0, 8), substr($hex, 8, 4), '4' . substr($hex, 12, 3), dechex(0x8 | hexdec($hex[15]) & 0x3) . $hex[16] . substr($hex, 17, 2), substr($hex, 19, 12)); }
/** * Handle redirection when the user needs to (re)authenticate. * * Send the user to the login form if needed; in case the request was a POST, stash in the * session and simulate it once the user gets back. * * @param string $subPage * @return bool False if execution should be stopped. * @throws ErrorPageError When the user is not allowed to use this page. */ protected function handleReauthBeforeExecute($subPage) { $authManager = AuthManager::singleton(); $request = $this->getRequest(); $key = 'AuthManagerSpecialPage:reauth:' . $this->getName(); $securityLevel = $this->getLoginSecurityLevel(); if ($securityLevel) { $securityStatus = AuthManager::singleton()->securitySensitiveOperationStatus($securityLevel); if ($securityStatus === AuthManager::SEC_REAUTH) { $queryParams = array_diff_key($request->getQueryValues(), ['title' => true]); if ($request->wasPosted()) { // unique ID in case the same special page is open in multiple browser tabs $uniqueId = MWCryptRand::generateHex(6); $key = $key . ':' . $uniqueId; $queryParams = ['authUniqueId' => $uniqueId] + $queryParams; $authData = array_diff_key($request->getValues(), $this->getPreservedParams(false), ['title' => 1]); $authManager->setAuthenticationSessionData($key, $authData); } $title = SpecialPage::getTitleFor('Userlogin'); $url = $title->getFullURL(['returnto' => $this->getFullTitle()->getPrefixedDBkey(), 'returntoquery' => wfArrayToCgi($queryParams), 'force' => $securityLevel], false, PROTO_HTTPS); $this->getOutput()->redirect($url); return false; } elseif ($securityStatus !== AuthManager::SEC_OK) { throw new ErrorPageError('cannotauth-not-allowed-title', 'cannotauth-not-allowed'); } } $uniqueId = $request->getVal('authUniqueId'); if ($uniqueId) { $key = $key . ':' . $uniqueId; $authData = $authManager->getAuthenticationSessionData($key); if ($authData) { $authManager->removeAuthenticationSessionData($key); $this->setRequest($authData, true); } } return true; }
/** * Generate a random string suitable for a password * * @param int $minLength Minimum length of password to generate * @return string */ public static function generateRandomPasswordString($minLength = 10) { // Decide the final password length based on our min password length, // stopping at a minimum of 10 chars. $length = max(10, $minLength); // Multiply by 1.25 to get the number of hex characters we need // Generate random hex chars $hex = MWCryptRand::generateHex(ceil($length * 1.25)); // Convert from base 16 to base 32 to get a proper password like string return substr(Wikimedia\base_convert($hex, 16, 32, $length), -$length); }
/** * Generate a secret value for variables using our CryptRand generator. * Produce a warning if the random source was insecure. * * @param $keys Array * @return Status */ protected function doGenerateKeys($keys) { $status = Status::newGood(); $strong = true; foreach ($keys as $name => $length) { $secretKey = MWCryptRand::generateHex($length, true); if (!MWCryptRand::wasStrong()) { $strong = false; } $this->setVar($name, $secretKey); } if (!$strong) { $names = array_keys($keys); $names = preg_replace('/^(.*)$/', '\\$$1', $names); global $wgLang; $status->warning('config-insecure-keys', $wgLang->listToText($names), count($names)); } return $status; }
/** * Return an RFC4122 compliant v4 UUID * * @param $flags integer Bitfield (supports UIDGenerator::QUICK_RAND) * @return string * @throws MWException */ public static function newUUIDv4( $flags = 0 ) { $hex = ( $flags & self::QUICK_RAND ) ? wfRandomString( 31 ) : MWCryptRand::generateHex( 31 ); return sprintf( '%s-%s-%s-%s-%s', // "time_low" (32 bits) substr( $hex, 0, 8 ), // "time_mid" (16 bits) substr( $hex, 8, 4 ), // "time_hi_and_version" (16 bits) '4' . substr( $hex, 12, 3 ), // "clk_seq_hi_res (8 bits, variant is binary 10x) and "clk_seq_low" (8 bits) dechex( 0x8 | ( hexdec( $hex[15] ) & 0x3 ) ) . $hex[16] . substr( $hex, 17, 2 ), // "node" (48 bits) substr( $hex, 19, 12 ) ); }
/** * MW specific salt, cached from last run * @return string Binary string */ protected function getSaltUsingCache() { if ($this->salt == '') { $lastSalt = $this->cache->get($this->cacheKey); if ($lastSalt === false) { // If we don't have a previous value to use as our salt, we use // 16 bytes from MWCryptRand, which will use a small amount of // entropy from our pool. Note, "XTR may be deterministic or keyed // via an optional “salt value” (i.e., a non-secret random // value)..." - http://eprint.iacr.org/2010/264.pdf. However, we // use a strongly random value since we can. $lastSalt = MWCryptRand::generate(16); } // Get a binary string that is hashLen long $this->salt = hash($this->algorithm, $lastSalt, true); } return $this->salt; }
/** * @return Title */ function scratchTitle() { return Title::makeTitle(NS_LQT_THREAD, MWCryptRand::generateHex(32)); }
/** * Get a unique, stable identifier for this wiki * * If the identifier does not already exist, create it and save it in the * database. The identifier is randomly-generated. * * @return string 32-character hex string */ private function getOrCreatePingbackId() { if (!$this->id) { $id = wfGetDB(DB_REPLICA)->selectField('updatelog', 'ul_value', ['ul_key' => 'PingBack']); if ($id == false) { $id = MWCryptRand::generateHex(32); $dbw = wfGetDB(DB_MASTER); $dbw->insert('updatelog', ['ul_key' => 'PingBack', 'ul_value' => $id], __METHOD__, 'IGNORE'); if (!$dbw->affectedRows()) { $id = $dbw->selectField('updatelog', 'ul_value', ['ul_key' => 'PingBack']); } } $this->id = $id; } return $this->id; }
/** * Override session_id before session startup if php's built-in * session generation code is not secure. */ function wfFixSessionID() { // If the cookie or session id is already set we already have a session and should abort if (isset($_COOKIE[session_name()]) || session_id()) { return; } // PHP's built-in session entropy is enabled if: // - entropy_file is set or you're on Windows with php 5.3.3+ // - AND entropy_length is > 0 // We treat it as disabled if it doesn't have an entropy length of at least 32 $entropyEnabled = (wfIsWindows() && version_compare(PHP_VERSION, '5.3.3', '>=') || ini_get('session.entropy_file')) && intval(ini_get('session.entropy_length')) >= 32; // If built-in entropy is not enabled or not sufficient override php's built in session id generation code if (!$entropyEnabled) { wfDebug(__METHOD__ . ": PHP's built in entropy is disabled or not sufficient, overriding session id generation using our cryptrand source.\n"); session_id(MWCryptRand::generateHex(32)); } }
/** * Gets a 32 character alphanumeric random string to be used for stats. * @return string */ private static function getEditingStatsId() { if (self::$statsId) { return self::$statsId; } return self::$statsId = MWCryptRand::generateHex(32); }
/** * Reset the session_id * * @since 1.22 */ function wfResetSessionID() { global $wgCookieSecure; $oldSessionId = session_id(); $cookieParams = session_get_cookie_params(); if (wfCheckEntropy() && $wgCookieSecure == $cookieParams['secure']) { session_regenerate_id(false); } else { $tmp = $_SESSION; session_destroy(); wfSetupSession(MWCryptRand::generateHex(32)); $_SESSION = $tmp; } $newSessionId = session_id(); Hooks::run('ResetSessionID', array($oldSessionId, $newSessionId)); }
/** * Make a new-style password hash * * @param $password String Plain-text password * @param bool|string $salt Optional salt, may be random or the user ID. * If unspecified or false, will generate one automatically * @return String Password hash */ public static function crypt($password, $salt = false) { global $wgPasswordSalt; $hash = ''; if (!wfRunHooks('UserCryptPassword', array(&$password, &$salt, &$wgPasswordSalt, &$hash))) { return $hash; } if ($wgPasswordSalt) { if ($salt === false) { $salt = MWCryptRand::generateHex(8); } return ':B:' . $salt . ':' . md5($salt . '-' . md5($password)); } else { return ':A:' . md5($password); } }
/** * Randomly generate a new createaccount token and attach it to the current session */ public static function setCreateaccountToken() { global $wgRequest; $wgRequest->setSessionData('wsCreateaccountToken', MWCryptRand::generateHex(32)); }
/** * Create an API centralauth token * @return string|bool Token */ static function getApiCentralAuthToken() { global $wgUser; if (!$wgUser->isAnon() && !self::hasApiToken()) { $centralUser = CentralAuthUser::getInstance($wgUser); if ($centralUser->exists() && $centralUser->isAttached()) { $data = array('userName' => $wgUser->getName(), 'token' => $centralUser->getAuthToken()); global $wgMemc; $loginToken = MWCryptRand::generateHex(32) . dechex($centralUser->getId()); $key = CentralAuthUser::memcKey('api-token', $loginToken); $wgMemc->add($key, $data, 60); return $loginToken; } } return false; }
/** * Save the BotPassword to the database * @param string $operation 'update' or 'insert' * @param Password|null $password Password to set. * @return bool Success */ public function save($operation, Password $password = null) { $conds = array('bp_user' => $this->centralId, 'bp_app_id' => $this->appId); $fields = array('bp_token' => MWCryptRand::generateHex(User::TOKEN_LENGTH), 'bp_restrictions' => $this->restrictions->toJson(), 'bp_grants' => FormatJson::encode($this->grants)); if ($password !== null) { $fields['bp_password'] = $password->toString(); } elseif ($operation === 'insert') { $fields['bp_password'] = PasswordFactory::newInvalidPassword()->toString(); } $dbw = self::getDB(DB_MASTER); switch ($operation) { case 'insert': $dbw->insert('bot_passwords', $fields + $conds, __METHOD__, array('IGNORE')); break; case 'update': $dbw->update('bot_passwords', $fields, $conds, __METHOD__); break; default: return false; } $ok = (bool) $dbw->affectedRows(); if ($ok) { $this->token = $dbw->selectField('bot_passwords', 'bp_token', $conds, __METHOD__); $this->isSaved = true; } return $ok; }
/** * Generate a new random session ID * @return string */ public function generateSessionId() { do { $id = wfBaseConvert(\MWCryptRand::generateHex(40), 16, 32, 32); $key = wfMemcKey('MWSession', $id); } while (isset($this->allSessionIds[$id]) || is_array($this->store->get($key))); return $id; }
/** * Renew the user's session id, using strong entropy */ private function renewSessionId() { global $wgSecureLogin, $wgCookieSecure; if ($wgSecureLogin && !$this->mStickHTTPS) { $wgCookieSecure = false; } // If either we don't trust PHP's entropy, or if we need // to change cookie settings when logging in because of // wpStickHTTPS, then change the session ID manually. $cookieParams = session_get_cookie_params(); if (wfCheckEntropy() && $wgCookieSecure == $cookieParams['secure']) { session_regenerate_id(false); } else { $tmp = $_SESSION; session_destroy(); wfSetupSession(MWCryptRand::generateHex(32)); $_SESSION = $tmp; } }