1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-30 02:32:42 +01:00

Use phutil_hashes_are_identical() when comparing hashes in Phabricator

Summary: See D14025. In all cases where we compare hashes, use strict, constant-time comparisons.

Test Plan: Logged in, logged out, added TOTP, ran Conduit, terminated sessions, submitted forms, changed password. Tweaked CSRF token, got rejected.

Reviewers: chad

Reviewed By: chad

Subscribers: chenxiruanhai

Differential Revision: https://secure.phabricator.com/D14026
This commit is contained in:
epriestley 2015-09-01 15:52:44 -07:00
parent 13516cf35f
commit 29948eaa5b
11 changed files with 33 additions and 35 deletions

View file

@ -209,7 +209,7 @@ abstract class PhabricatorAuthController extends PhabricatorController {
$actual = $account->getProperty('registrationKey');
$expect = PhabricatorHash::digest($registration_key);
if ($actual !== $expect) {
if (!phutil_hashes_are_identical($actual, $expect)) {
$response = $this->renderError(
pht(
'Your browser submitted a different registration key than the one '.

View file

@ -21,7 +21,10 @@ final class PhabricatorAuthTerminateSessionController
$sessions = $query->execute();
foreach ($sessions as $key => $session) {
if ($session->getSessionKey() == $current_key) {
$is_current = phutil_hashes_are_identical(
$session->getSessionKey(),
$current_key);
if ($is_current) {
// Don't terminate the current login session.
unset($sessions[$key]);
}

View file

@ -296,7 +296,10 @@ final class PhabricatorAuthSessionEngine extends Phobject {
foreach ($sessions as $key => $session) {
if ($except_session !== null) {
if ($except_session == $session->getSessionKey()) {
$is_except = phutil_hashes_are_identical(
$session->getSessionKey(),
$except_session);
if ($is_except) {
continue;
}
}

View file

@ -201,7 +201,7 @@ final class PhabricatorTOTPAuthFactor extends PhabricatorAuthFactor {
// case the server or client has some clock skew.
for ($offset = -2; $offset <= 2; $offset++) {
$real = self::getTOTPCode($key, $now + $offset);
if ($real === $code) {
if (phutil_hashes_are_identical($real, $code)) {
return true;
}
}

View file

@ -482,7 +482,7 @@ abstract class PhabricatorAuthProvider extends Phobject {
'problem persists, you may need to clear your cookies.'));
}
if ($actual !== $expect) {
if (!phutil_hashes_are_identical($actual, $expect)) {
throw new Exception(
pht(
'The authentication provider did not return the correct client '.

View file

@ -434,7 +434,8 @@ final class PhabricatorConduitAPIController
$token = idx($metadata, 'authToken');
$signature = idx($metadata, 'authSignature');
$certificate = $user->getConduitCertificate();
if (sha1($token.$certificate) !== $signature) {
$hash = sha1($token.$certificate);
if (!phutil_hashes_are_identical($hash, $signature)) {
return array(
'ERR-INVALID-AUTH',
pht('Authentication is invalid.'),

View file

@ -142,7 +142,7 @@ final class ConduitConnectConduitAPIMethod extends ConduitAPIMethod {
$threshold));
}
$valid = sha1($token.$user->getConduitCertificate());
if ($valid != $signature) {
if (!phutil_hashes_are_identical($valid, $signature)) {
throw new ConduitException('ERR-INVALID-CERTIFICATE');
}
$session_key = id(new PhabricatorAuthSessionEngine())->establishSession(

View file

@ -13,9 +13,11 @@ final class PhabricatorMetaMTAMailgunReceiveController
$timestamp = $request->getStr('timestamp');
$token = $request->getStr('token');
$sig = $request->getStr('signature');
return hash_hmac('sha256', $timestamp.$token, $api_key) == $sig;
$hash = hash_hmac('sha256', $timestamp.$token, $api_key);
return phutil_hashes_are_identical($sig, $hash);
}
public function processRequest() {
// No CSRF for Mailgun.

View file

@ -365,19 +365,16 @@ final class PhabricatorUser
}
public function validateCSRFToken($token) {
$salt = null;
$version = 'plain';
// This is a BREACH-mitigating token. See T3684.
// We expect a BREACH-mitigating token. See T3684.
$breach_prefix = self::CSRF_BREACH_PREFIX;
$breach_prelen = strlen($breach_prefix);
if (!strncmp($token, $breach_prefix, $breach_prelen)) {
$version = 'breach';
$salt = substr($token, $breach_prelen, self::CSRF_SALT_LENGTH);
$token = substr($token, $breach_prelen + self::CSRF_SALT_LENGTH);
if (strncmp($token, $breach_prefix, $breach_prelen) !== 0) {
return false;
}
$salt = substr($token, $breach_prelen, self::CSRF_SALT_LENGTH);
$token = substr($token, $breach_prelen + self::CSRF_SALT_LENGTH);
// When the user posts a form, we check that it contains a valid CSRF token.
// Tokens cycle each hour (every CSRF_CYLCE_FREQUENCY seconds) and we accept
// either the current token, the next token (users can submit a "future"
@ -407,22 +404,11 @@ final class PhabricatorUser
for ($ii = -$csrf_window; $ii <= 1; $ii++) {
$valid = $this->getRawCSRFToken($ii);
switch ($version) {
// TODO: We can remove this after the BREACH version has been in the
// wild for a while.
case 'plain':
if ($token == $valid) {
return true;
}
break;
case 'breach':
$digest = PhabricatorHash::digest($valid, $salt);
if (substr($digest, 0, self::CSRF_TOKEN_LENGTH) == $token) {
return true;
}
break;
default:
throw new Exception(pht('Unknown CSRF token format!'));
$digest = PhabricatorHash::digest($valid, $salt);
$digest = substr($digest, 0, self::CSRF_TOKEN_LENGTH);
if (phutil_hashes_are_identical($digest, $token)) {
return true;
}
}

View file

@ -50,7 +50,10 @@ final class PhabricatorSessionsSettingsPanel extends PhabricatorSettingsPanel {
$rows = array();
$rowc = array();
foreach ($sessions as $session) {
if ($session->getSessionKey() == $current_key) {
$is_current = phutil_hashes_are_identical(
$session->getSessionKey(),
$current_key);
if ($is_current) {
$rowc[] = 'highlighted';
$button = phutil_tag(
'a',

View file

@ -126,7 +126,7 @@ abstract class PhabricatorPasswordHasher extends Phobject {
$actual_hash = $this->getPasswordHash($password)->openEnvelope();
$expect_hash = $hash->openEnvelope();
return ($actual_hash === $expect_hash);
return phutil_hashes_are_identical($actual_hash, $expect_hash);
}