1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-22 14:52:41 +01:00

Move most remaining sha1() calls to HMAC

Summary:
  - For context, see T547. This is the last (maybe?) in a series of diffs that
moves us off raw sha1() calls in order to make it easier to audit the codebase
for correct use of hash functions.
  - This breaks CSRF tokens. Any open forms will generate an error when
submitted, so maybe upgrade off-peak.
  - We now generate HMAC mail keys but accept MAC or HMAC. In a few months, we
can remove the MAC version.
  - The only remaining callsite is Conduit. We can't use HMAC since Arcanist
would need to know the key. {T550} provides a better solution to this, anyway.

Test Plan:
  - Verified CSRF tokens generate properly.
  - Manually changed CSRF to an incorrect value and got an error.
  - Verified mail generates with a new mail hash.
  - Verified Phabricator accepts both old and new mail hashes.
  - Verified Phabricator rejects bad mail hashes.
  - Checked user log, things look OK.

Reviewers: btrahan, jungejason, benmathews

Reviewed By: btrahan

CC: aran, epriestley, btrahan

Maniphest Tasks: T547

Differential Revision: 1237
This commit is contained in:
epriestley 2011-12-18 11:00:39 -08:00
parent 5f1b3937e5
commit e45ffda55a
11 changed files with 75 additions and 4 deletions

View file

@ -65,6 +65,12 @@ return array(
// attack difficult, but it is viable unless you isolate the file domain. // attack difficult, but it is viable unless you isolate the file domain.
'security.alternate-file-domain' => null, 'security.alternate-file-domain' => null,
// Default key for HMAC digests where the key is not important (i.e., the
// hash itself is secret). You can change this if you want (to any other
// string), but doing so will break existing sessions and CSRF tokens.
'security.hmac-key' => '[D\t~Y7eNmnQGJ;rnH6aF;m2!vJ8@v8C=Cs:aQS\.Qw',
// -- DarkConsole ----------------------------------------------------------- // // -- DarkConsole ----------------------------------------------------------- //
// DarkConsole is a administrative debugging/profiling tool built into // DarkConsole is a administrative debugging/profiling tool built into

View file

@ -471,6 +471,7 @@ phutil_register_library_map(array(
'PhabricatorGarbageCollectorDaemon' => 'infrastructure/daemon/garbagecollector', 'PhabricatorGarbageCollectorDaemon' => 'infrastructure/daemon/garbagecollector',
'PhabricatorGoodForNothingWorker' => 'infrastructure/daemon/workers/worker/goodfornothing', 'PhabricatorGoodForNothingWorker' => 'infrastructure/daemon/workers/worker/goodfornothing',
'PhabricatorHandleObjectSelectorDataView' => 'applications/phid/handle/view/selector', 'PhabricatorHandleObjectSelectorDataView' => 'applications/phid/handle/view/selector',
'PhabricatorHash' => 'infrastructure/util/hash',
'PhabricatorHelpController' => 'applications/help/controller/base', 'PhabricatorHelpController' => 'applications/help/controller/base',
'PhabricatorHelpKeyboardShortcutController' => 'applications/help/controller/keyboardshortcut', 'PhabricatorHelpKeyboardShortcutController' => 'applications/help/controller/keyboardshortcut',
'PhabricatorIRCBot' => 'infrastructure/daemon/irc/bot', 'PhabricatorIRCBot' => 'infrastructure/daemon/irc/bot',

View file

@ -176,7 +176,11 @@ class PhabricatorMetaMTAReceivedMail extends PhabricatorMetaMTADAO {
} }
$expect_hash = self::computeMailHash($receiver->getMailKey(), $check_phid); $expect_hash = self::computeMailHash($receiver->getMailKey(), $check_phid);
if ($expect_hash != $hash) {
// See note at computeOldMailHash().
$old_hash = self::computeOldMailHash($receiver->getMailKey(), $check_phid);
if ($expect_hash != $hash && $old_hash != $hash) {
return $this->setMessage("Invalid mail hash!")->save(); return $this->setMessage("Invalid mail hash!")->save();
} }
@ -228,6 +232,20 @@ class PhabricatorMetaMTAReceivedMail extends PhabricatorMetaMTADAO {
public static function computeMailHash($mail_key, $phid) { public static function computeMailHash($mail_key, $phid) {
$global_mail_key = PhabricatorEnv::getEnvConfig('phabricator.mail-key'); $global_mail_key = PhabricatorEnv::getEnvConfig('phabricator.mail-key');
$hash = PhabricatorHash::digest($mail_key.$global_mail_key.$phid);
return substr($hash, 0, 16);
}
public static function computeOldMailHash($mail_key, $phid) {
// TODO: Remove this method entirely in a couple of months. We've moved from
// plain sha1 to sha1+hmac to make the codebase more auditable for good uses
// of hash functions, but still accept the old hashes on email replies to
// avoid breaking things. Once we've been sending only hmac hashes for a
// while, remove this and start rejecting old hashes. See T547.
$global_mail_key = PhabricatorEnv::getEnvConfig('phabricator.mail-key');
$hash = sha1($mail_key.$global_mail_key.$phid); $hash = sha1($mail_key.$global_mail_key.$phid);
return substr($hash, 0, 16); return substr($hash, 0, 16);
} }

View file

@ -14,6 +14,7 @@ phutil_require_module('phabricator', 'applications/metamta/parser');
phutil_require_module('phabricator', 'applications/metamta/storage/base'); phutil_require_module('phabricator', 'applications/metamta/storage/base');
phutil_require_module('phabricator', 'applications/people/storage/user'); phutil_require_module('phabricator', 'applications/people/storage/user');
phutil_require_module('phabricator', 'infrastructure/env'); phutil_require_module('phabricator', 'infrastructure/env');
phutil_require_module('phabricator', 'infrastructure/util/hash');
phutil_require_module('phutil', 'utils'); phutil_require_module('phutil', 'utils');

View file

@ -89,7 +89,7 @@ class PhabricatorUserLog extends PhabricatorUserDAO {
// seeing the logs doesn't compromise all the sessions which appear in // seeing the logs doesn't compromise all the sessions which appear in
// them. This just prevents casual leaks, like in a screenshot. // them. This just prevents casual leaks, like in a screenshot.
if (strlen($session)) { if (strlen($session)) {
$this->session = sha1($session); $this->session = PhabricatorHash::digest($session);
} }
return $this; return $this;
} }

View file

@ -7,6 +7,7 @@
phutil_require_module('phabricator', 'applications/people/storage/base'); phutil_require_module('phabricator', 'applications/people/storage/base');
phutil_require_module('phabricator', 'infrastructure/util/hash');
phutil_require_module('phutil', 'utils'); phutil_require_module('phutil', 'utils');

View file

@ -191,7 +191,7 @@ class PhabricatorUser extends PhabricatorUserDAO {
private function generateToken($epoch, $frequency, $key, $len) { private function generateToken($epoch, $frequency, $key, $len) {
$time_block = floor($epoch / $frequency); $time_block = floor($epoch / $frequency);
$vec = $this->getPHID().$this->getPasswordHash().$key.$time_block; $vec = $this->getPHID().$this->getPasswordHash().$key.$time_block;
return substr(sha1($vec), 0, $len); return substr(PhabricatorHash::digest($vec), 0, $len);
} }
/** /**

View file

@ -14,6 +14,7 @@ phutil_require_module('phabricator', 'applications/phid/constants');
phutil_require_module('phabricator', 'applications/phid/storage/phid'); phutil_require_module('phabricator', 'applications/phid/storage/phid');
phutil_require_module('phabricator', 'applications/search/index/indexer/user'); phutil_require_module('phabricator', 'applications/search/index/indexer/user');
phutil_require_module('phabricator', 'infrastructure/env'); phutil_require_module('phabricator', 'infrastructure/env');
phutil_require_module('phabricator', 'infrastructure/util/hash');
phutil_require_module('phabricator', 'storage/qsprintf'); phutil_require_module('phabricator', 'storage/qsprintf');
phutil_require_module('phabricator', 'storage/queryfx'); phutil_require_module('phabricator', 'storage/queryfx');

View file

@ -58,7 +58,7 @@ of 7-character hashes:
Because 7-character hashes are likely to collide for even moderately large Because 7-character hashes are likely to collide for even moderately large
repositories, Diffusion generally uses either a 16-character prefix (which makes repositories, Diffusion generally uses either a 16-character prefix (which makes
collisions very unlikely) or the full 40-character SHA1 (which makes collisions collisions very unlikely) or the full 40-character hash (which makes collisions
astronomically unlikely). astronomically unlikely).
= Adding Repositories = = Adding Repositories =

View file

@ -0,0 +1,31 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorHash {
public static function digest($string) {
$key = PhabricatorEnv::getEnvConfig('security.hmac-key');
if (!$key) {
throw new Exception(
"Set a 'security.hmac-key' in your Phabricator configuration!");
}
return hash_hmac('sha1', $string, $key);
}
}

View file

@ -0,0 +1,12 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'infrastructure/env');
phutil_require_source('PhabricatorHash.php');