@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Upgrade an old "weakDigest()" inside TOTP synchronization code

Summary:
Ref T13222. Ref T12509. When you add a new MFA TOTP authenticator, we generate a temporary token to make sure you're actually adding the key we generated and not picking your own key.

That is, if we just put inputs in the form like `key=123, response=456`, users could pick their own keys by changing the value of `key` and then generating the correct `response`. That's probably fine, but maybe attackers could somehow force users to pick known keys in combination with other unknown vulnerabilities that might exist in the future. Instead, we generate a random key and keep track of it to make sure nothing funny is afoot.

As an additional barrier, we do the standard "store the digest, not the real key" sort of thing so you can't force a known value even if you can read the database (although this is mostly pointless since you can just read TOTP secrets directly if you can read the database). But it's pretty standard and doesn't hurt anything.

Update this from SHA1 to SHA256. This will break any TOTP factors which someone was in the middle of adding during a Phabricator upgrade, but that seems reasonable. They'll get a sensible failure mode.

Test Plan: Added a new TOTP factor.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T13222, T12509

Differential Revision: https://secure.phabricator.com/D19884

+12 -2
+12 -2
src/applications/auth/factor/PhabricatorTOTPAuthFactor.php
··· 2 2 3 3 final class PhabricatorTOTPAuthFactor extends PhabricatorAuthFactor { 4 4 5 + const DIGEST_TEMPORARY_KEY = 'mfa.totp.sync'; 6 + 5 7 public function getFactorKey() { 6 8 return 'totp'; 7 9 } ··· 34 36 // (We store and verify the hash of the key, not the key itself, to limit 35 37 // how useful the data in the table is to an attacker.) 36 38 39 + $token_code = PhabricatorHash::digestWithNamedKey( 40 + $key, 41 + self::DIGEST_TEMPORARY_KEY); 42 + 37 43 $temporary_token = id(new PhabricatorAuthTemporaryTokenQuery()) 38 44 ->setViewer($user) 39 45 ->withTokenResources(array($user->getPHID())) 40 46 ->withTokenTypes(array($totp_token_type)) 41 47 ->withExpired(false) 42 - ->withTokenCodes(array(PhabricatorHash::weakDigest($key))) 48 + ->withTokenCodes(array($token_code)) 43 49 ->executeOne(); 44 50 if (!$temporary_token) { 45 51 // If we don't have a matching token, regenerate the key below. ··· 53 59 // Mark this key as one we generated, so the user is allowed to submit 54 60 // a response for it. 55 61 62 + $token_code = PhabricatorHash::digestWithNamedKey( 63 + $key, 64 + self::DIGEST_TEMPORARY_KEY); 65 + 56 66 $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 57 67 id(new PhabricatorAuthTemporaryToken()) 58 68 ->setTokenResource($user->getPHID()) 59 69 ->setTokenType($totp_token_type) 60 70 ->setTokenExpires(time() + phutil_units('1 hour in seconds')) 61 - ->setTokenCode(PhabricatorHash::weakDigest($key)) 71 + ->setTokenCode($token_code) 62 72 ->save(); 63 73 unset($unguarded); 64 74 }