From: Brad Jorsch Date: Mon, 1 Feb 2016 20:07:09 +0000 (-0500) Subject: Add $wgAuthenticationTokenVersion X-Git-Tag: 1.31.0-rc.0~8109 X-Git-Url: http://git.cyclocoop.org/%22.%24image2.%22?a=commitdiff_plain;h=fbec46e3089ead3c39994d55ea6bfe0863eddfd6;p=lhc%2Fweb%2Fwiklou.git Add $wgAuthenticationTokenVersion This allows for quickly invalidating everyone's session all at once by changing a single value. As a side effect, setting this also stops the user_token field from the database from being served to the user as a cookie. This mitigates but doesn't completely solve T49490, as it allows for invalidating all existing sessions and token-cookies but does not help if the user_token field in the database was leaked. Bug: T49490 Change-Id: I9d316a6bbb36278d138f39a89125ebb8cc71b28f --- diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index f30854aaf1..2b3e6e2e70 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -4633,6 +4633,18 @@ $wgUserrightsInterwikiDelimiter = '@'; */ $wgSecureLogin = false; +/** + * Versioning for authentication tokens. + * + * If non-null, this is combined with the user's secret (the user_token field + * in the DB) to generate the token cookie. Changing this will invalidate all + * active sessions (i.e. it will log everyone out). + * + * @since 1.27 + * @var string|null + */ +$wgAuthenticationTokenVersion = null; + /** @} */ # end user accounts } /************************************************************************//** diff --git a/includes/user/User.php b/includes/user/User.php index d71e5e1f11..ac78abe9f4 100644 --- a/includes/user/User.php +++ b/includes/user/User.php @@ -1184,7 +1184,7 @@ class User implements IDBAccessObject { if ( ( $sName === $proposedUser->getName() ) && $passwordCorrect ) { $this->loadFromUserObject( $proposedUser ); - $request->setSessionData( 'wsToken', $this->mToken ); + $request->setSessionData( 'wsToken', $this->getToken( false ) ); wfDebug( "User: logged in from $from\n" ); return true; } else { @@ -2457,14 +2457,33 @@ class User implements IDBAccessObject { * Get the user's current token. * @param bool $forceCreation Force the generation of a new token if the * user doesn't have one (default=true for backwards compatibility). - * @return string Token + * @return string|null Token */ public function getToken( $forceCreation = true ) { + global $wgAuthenticationTokenVersion; + $this->load(); if ( !$this->mToken && $forceCreation ) { $this->setToken(); } - return $this->mToken; + + // If the user doesn't have a token, return null to indicate that. + // Otherwise, hmac the version with the secret if we have a version. + if ( !$this->mToken ) { + return null; + } elseif ( $wgAuthenticationTokenVersion === null ) { + return $this->mToken; + } else { + $ret = MWCryptHash::hmac( $wgAuthenticationTokenVersion, $this->mToken, false ); + + // The raw hash can be overly long. Shorten it up. + $len = max( 32, self::TOKEN_LENGTH ); + if ( strlen( $ret ) < $len ) { + // Should never happen, even md5 is 128 bits + throw new \UnexpectedValueException( 'Hmac returned less than 128 bits' ); + } + return substr( $ret, -$len ); + } } /** @@ -3595,7 +3614,7 @@ class User implements IDBAccessObject { } $session = array( 'wsUserID' => $this->mId, - 'wsToken' => $this->mToken, + 'wsToken' => $this->getToken( false ), 'wsUserName' => $this->getName() ); $cookies = array( @@ -3603,7 +3622,7 @@ class User implements IDBAccessObject { 'UserName' => $this->getName(), ); if ( $rememberMe ) { - $cookies['Token'] = $this->mToken; + $cookies['Token'] = $this->getToken( false ); } else { $cookies['Token'] = false; }