X-Git-Url: https://scripts.mit.edu/gitweb/autoinstalls/mediawiki.git/blobdiff_plain/18a6620945d02687fbcfc4c27355d952fd748b41..19e297c21b10b1b8a3acad5e73fc71dcb35db44a:/includes/User.php diff --git a/includes/User.php b/includes/User.php index 5760003b..c0de2023 100644 --- a/includes/User.php +++ b/includes/User.php @@ -99,7 +99,6 @@ class User { 'deletedhistory', 'deletedtext', 'deleterevision', - 'disableaccount', 'edit', 'editinterface', 'editusercssjs', #deprecated @@ -787,23 +786,20 @@ class User { } /** - * Return a random password. Sourced from mt_rand, so it's not particularly secure. - * @todo hash random numbers to improve security, like generateToken() + * Return a random password. * * @return \string New random password */ static function randomPassword() { - global $wgMinimalPasswordLength; - $pwchars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz'; - $l = strlen( $pwchars ) - 1; - - $pwlength = max( 7, $wgMinimalPasswordLength ); - $digit = mt_rand( 0, $pwlength - 1 ); - $np = ''; - for ( $i = 0; $i < $pwlength; $i++ ) { - $np .= $i == $digit ? chr( mt_rand( 48, 57 ) ) : $pwchars{ mt_rand( 0, $l ) }; - } - return $np; + global $wgMinimalPasswordLength; + // Decide the final password length based on our min password length, stopping at a minimum of 10 chars + $length = max( 10, $wgMinimalPasswordLength ); + // Multiply by 1.25 to get the number of hex characters we need + $length = $length * 1.25; + // Generate random hex chars + $hex = MWCryptRand::generateHex( $length ); + // Convert from base 16 to base 32 to get a proper password like string + return wfBaseConvert( $hex, 16, 32 ); } /** @@ -833,7 +829,7 @@ class User { $this->mTouched = '0'; # Allow any pages to be cached } - $this->setToken(); # Random + $this->mToken = null; // Don't run cryptographic functions till we need a token $this->mEmailAuthenticated = null; $this->mEmailToken = ''; $this->mEmailTokenExpires = null; @@ -920,11 +916,11 @@ class User { return false; } - if ( isset( $_SESSION['wsToken'] ) ) { - $passwordCorrect = $proposedUser->getToken() === $_SESSION['wsToken']; + if ( isset( $_SESSION['wsToken'] ) && $_SESSION['wsToken'] ) { + $passwordCorrect = $proposedUser->getToken( false ) === $_SESSION['wsToken']; $from = 'session'; - } else if ( $wgRequest->getCookie( 'Token' ) !== null ) { - $passwordCorrect = $proposedUser->getToken() === $wgRequest->getCookie( 'Token' ); + } elseif ( $wgRequest->getCookie( 'Token' ) ) { + $passwordCorrect = $proposedUser->getToken( false ) === $wgRequest->getCookie( 'Token' ); $from = 'cookie'; } else { # No session or persistent login cookie @@ -1013,6 +1009,9 @@ class User { $this->decodeOptions( $row->user_options ); $this->mTouched = wfTimestamp(TS_MW,$row->user_touched); $this->mToken = $row->user_token; + if ( $this->mToken == '' ) { + $this->mToken = null; + } $this->mEmailAuthenticated = wfTimestampOrNull( TS_MW, $row->user_email_authenticated ); $this->mEmailToken = $row->user_email_token; $this->mEmailTokenExpires = wfTimestampOrNull( TS_MW, $row->user_email_token_expires ); @@ -1842,10 +1841,14 @@ class User { /** * Get the user's current token. + * @param $forceCreation Force the generation of a new token if the user doesn't have one (default=true for backwards compatibility) * @return \string Token */ - function getToken() { + function getToken( $forceCreation = true ) { $this->load(); + if ( !$this->mToken && $forceCreation ) { + $this->setToken(); + } return $this->mToken; } @@ -1860,14 +1863,7 @@ class User { global $wgSecretKey, $wgProxyKey; $this->load(); if ( !$token ) { - if ( $wgSecretKey ) { - $key = $wgSecretKey; - } elseif ( $wgProxyKey ) { - $key = $wgProxyKey; - } else { - $key = microtime(); - } - $this->mToken = md5( $key . mt_rand( 0, 0x7fffffff ) . wfWikiID() . $this->mId ); + $this->mToken = MWCryptRand::generateHex( USER_TOKEN_LENGTH ); } else { $this->mToken = $token; } @@ -2478,6 +2474,14 @@ class User { function setCookies() { $this->load(); if ( 0 == $this->mId ) return; + if ( !$this->mToken ) { + // When token is empty or NULL generate a new one and then save it to the database + // This allows a wiki to re-secure itself after a leak of it's user table or $wgSecretKey + // Simply by setting every cell in the user_token column to NULL and letting them be + // regenerated as users log back into the wiki. + $this->setToken(); + $this->saveSettings(); + } $session = array( 'wsUserID' => $this->mId, 'wsToken' => $this->mToken, @@ -2556,7 +2560,7 @@ class User { 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ), 'user_options' => '', 'user_touched' => $dbw->timestamp( $this->mTouched ), - 'user_token' => $this->mToken, + 'user_token' => strval( $this->mToken ), 'user_email_token' => $this->mEmailToken, 'user_email_token_expires' => $dbw->timestampOrNull( $this->mEmailTokenExpires ), ), array( /* WHERE */ @@ -2622,7 +2626,7 @@ class User { 'user_email_authenticated' => $dbw->timestampOrNull( $user->mEmailAuthenticated ), 'user_real_name' => $user->mRealName, 'user_options' => '', - 'user_token' => $user->mToken, + 'user_token' => strval( $user->mToken ), 'user_registration' => $dbw->timestamp( $user->mRegistration ), 'user_editcount' => 0, ); @@ -2656,7 +2660,7 @@ class User { 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ), 'user_real_name' => $this->mRealName, 'user_options' => '', - 'user_token' => $this->mToken, + 'user_token' => strval( $this->mToken ), 'user_registration' => $dbw->timestamp( $this->mRegistration ), 'user_editcount' => 0, ), __METHOD__ @@ -2882,7 +2886,7 @@ class User { return EDIT_TOKEN_SUFFIX; } else { if( !isset( $_SESSION['wsEditToken'] ) ) { - $token = self::generateToken(); + $token = MWCryptRand::generateHex( 32 ); $_SESSION['wsEditToken'] = $token; } else { $token = $_SESSION['wsEditToken']; @@ -2901,8 +2905,7 @@ class User { * @return \string The new random token */ public static function generateToken( $salt = '' ) { - $token = dechex( mt_rand() ) . dechex( mt_rand() ); - return md5( $token . $salt ); + return MWCryptRand::generateHex( 32 ); } /** @@ -3007,7 +3010,7 @@ class User { $now = time(); $expires = $now + 7 * 24 * 60 * 60; $expiration = wfTimestamp( TS_MW, $expires ); - $token = self::generateToken( $this->mId . $this->mEmail . $expires ); + $token = MWCryptRand::generateHex( 32 ); $hash = md5( $token ); $this->load(); $this->mEmailToken = $hash; @@ -3561,7 +3564,7 @@ class User { if( $wgPasswordSalt ) { if ( $salt === false ) { - $salt = substr( wfGenerateToken(), 0, 8 ); + $salt = MWCryptRand::generateHex( 8 ); } return ':B:' . $salt . ':' . md5( $salt . '-' . md5( $password ) ); } else { @@ -3592,7 +3595,7 @@ class User { } elseif ( $type == ':B:' ) { # Salted list( $salt, $realHash ) = explode( ':', substr( $hash, 3 ), 2 ); - return md5( $salt.'-'.md5( $password ) ) == $realHash; + return md5( $salt.'-'.md5( $password ) ) === $realHash; } else { # Old-style return self::oldCrypt( $password, $userId ) === $hash;