@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.

Mark all existing password hashes as "legacy" and start upgrading digest formats

Summary:
Depends on D18907. Ref T13043. Ref T12509. We have some weird old password digest behavior that isn't terribly concerning, but also isn't great.

Specifically, old passwords were digested in weird ways before being hashed. Notably, account passwords were digested with usernames, so your password stops working if your username is chagned. Not the end of the world, but silly.

Mark all existing hashes as "v1", and automatically upgrade then when they're used or changed. Some day, far in the future, we could stop supporting these legacy digests and delete the code and passwords and just issue upgrade advice ("Passwords which haven't been used in more than two years no longer work."). But at least get things on a path toward sane, modern behavior.

Test Plan: Ran migration. Spot-checked that everthing in the database got marked as "v1". Used an existing password to login successfully. Verified that it was upgraded to a `null` (modern) digest. Logged in with it again.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T13043, T12509

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

+51 -33
+2
resources/sql/autopatches/20180121.auth.06.legacydigest.sql
··· 1 + ALTER TABLE {$NAMESPACE}_auth.auth_password 2 + ADD legacyDigestFormat VARCHAR(32) COLLATE {$COLLATE_TEXT};
+4
resources/sql/autopatches/20180121.auth.07.marklegacy.sql
··· 1 + UPDATE {$NAMESPACE}_auth.auth_password 2 + SET legacyDigestFormat = 'v1' 3 + WHERE passwordType IN ('vcs', 'account') 4 + AND legacyDigestFormat IS NULL;
+11
src/applications/auth/storage/PhabricatorAuthPassword.php
··· 12 12 protected $passwordHash; 13 13 protected $passwordSalt; 14 14 protected $isRevoked; 15 + protected $legacyDigestFormat; 15 16 16 17 private $object = self::ATTACHABLE; 17 18 ··· 38 39 'passwordHash' => 'text128', 39 40 'passwordSalt' => 'text64', 40 41 'isRevoked' => 'bool', 42 + 'legacyDigestFormat' => 'text32?', 41 43 ), 42 44 self::CONFIG_KEY_SCHEMA => array( 43 45 'key_role' => array( ··· 66 68 } 67 69 68 70 public function canUpgrade() { 71 + // If this password uses a legacy digest format, we can upgrade it to the 72 + // new digest format even if a better hasher isn't available. 73 + if ($this->getLegacyDigestFormat() !== null) { 74 + return true; 75 + } 76 + 69 77 $hash = $this->newPasswordEnvelope(); 70 78 return PhabricatorPasswordHasher::canUpgradeHash($hash); 71 79 } ··· 109 117 // Generate (or regenerate) the salt first. 110 118 $new_salt = Filesystem::readRandomCharacters(64); 111 119 $this->setPasswordSalt($new_salt); 120 + 121 + // Clear any legacy digest format to force a modern digest. 122 + $this->setLegacyDigestFormat(null); 112 123 113 124 $digest = $this->digestPassword($password, $object); 114 125 $hash = $hasher->getPasswordHashForStorage($digest);
+34 -33
src/applications/people/storage/PhabricatorUser.php
··· 1590 1590 // Applying salt while digesting passwords ensures that hashes are salted 1591 1591 // whether we ultimately select a self-salting hasher or not. 1592 1592 1593 - // For legacy compatibility reasons, the VCS and Account password digest 1593 + // For legacy compatibility reasons, old VCS and Account password digest 1594 1594 // algorithms are significantly more complicated than necessary to achieve 1595 1595 // these goals. This is because they once used a different hashing and 1596 1596 // salting process. When we upgraded to the modern modular hasher ··· 1602 1602 // everything that a digest callback should without any needless legacy 1603 1603 // baggage on top. 1604 1604 1605 - switch ($password->getPasswordType()) { 1606 - case PhabricatorAuthPassword::PASSWORD_TYPE_VCS: 1607 - // VCS passwords use an iterated HMAC SHA1 as a digest algorithm. They 1608 - // originally used this as a hasher, but it became a digest alorithm 1609 - // once hashing was upgraded to include bcrypt. 1610 - $digest = $envelope->openEnvelope(); 1611 - $salt = $this->getPHID(); 1612 - for ($ii = 0; $ii < 1000; $ii++) { 1613 - $digest = PhabricatorHash::weakDigest($digest, $salt); 1614 - } 1615 - return new PhutilOpaqueEnvelope($digest); 1616 - case PhabricatorAuthPassword::PASSWORD_TYPE_ACCOUNT: 1617 - // Account passwords use this weird mess of salt and do not digest 1618 - // the input to a standard length. 1605 + if ($password->getLegacyDigestFormat() == 'v1') { 1606 + switch ($password->getPasswordType()) { 1607 + case PhabricatorAuthPassword::PASSWORD_TYPE_VCS: 1608 + // Old VCS passwords use an iterated HMAC SHA1 as a digest algorithm. 1609 + // They originally used this as a hasher, but it became a digest 1610 + // algorithm once hashing was upgraded to include bcrypt. 1611 + $digest = $envelope->openEnvelope(); 1612 + $salt = $this->getPHID(); 1613 + for ($ii = 0; $ii < 1000; $ii++) { 1614 + $digest = PhabricatorHash::weakDigest($digest, $salt); 1615 + } 1616 + return new PhutilOpaqueEnvelope($digest); 1617 + case PhabricatorAuthPassword::PASSWORD_TYPE_ACCOUNT: 1618 + // Account passwords previously used this weird mess of salt and did 1619 + // not digest the input to a standard length. 1619 1620 1620 - // TODO: We should build a migration pathway forward from this which 1621 - // uses a better (HMAC SHA256) digest algorithm. Beyond this being 1622 - // a weird special case, there are two actual problems with this, 1623 - // although neither are particularly severe: 1621 + // Beyond this being a weird special case, there are two actual 1622 + // problems with this, although neither are particularly severe: 1624 1623 1625 - // First, because we do not normalize the length of passwords, this 1626 - // algorithm may make us vulnerable to DOS attacks where attacker 1627 - // attempt to use very long inputs to slow down hashers. 1624 + // First, because we do not normalize the length of passwords, this 1625 + // algorithm may make us vulnerable to DOS attacks where an attacker 1626 + // attempts to use a very long input to slow down hashers. 1628 1627 1629 - // Second, because the username is part of the hash algorithm, renaming 1630 - // a user breaks their password. This isn't a huge deal but it's pretty 1631 - // silly. There's no security justification for this behavior, I just 1632 - // didn't think about the implication when I wrote it originally. 1628 + // Second, because the username is part of the hash algorithm, 1629 + // renaming a user breaks their password. This isn't a huge deal but 1630 + // it's pretty silly. There's no security justification for this 1631 + // behavior, I just didn't think about the implication when I wrote 1632 + // it originally. 1633 1633 1634 - $parts = array( 1635 - $this->getUsername(), 1636 - $envelope->openEnvelope(), 1637 - $this->getPHID(), 1638 - $password->getPasswordSalt(), 1639 - ); 1634 + $parts = array( 1635 + $this->getUsername(), 1636 + $envelope->openEnvelope(), 1637 + $this->getPHID(), 1638 + $password->getPasswordSalt(), 1639 + ); 1640 1640 1641 - return new PhutilOpaqueEnvelope(implode('', $parts)); 1641 + return new PhutilOpaqueEnvelope(implode('', $parts)); 1642 + } 1642 1643 } 1643 1644 1644 1645 // For passwords which do not have some crazy legacy reason to use some