Changeset View
Changeset View
Standalone View
Standalone View
src/applications/people/storage/PhabricatorUser.php
Show First 20 Lines • Show All 257 Lines • ▼ Show 20 Lines | protected function getConfiguration() { | ||||
) + parent::getConfiguration(); | ) + parent::getConfiguration(); | ||||
} | } | ||||
public function generatePHID() { | public function generatePHID() { | ||||
return PhabricatorPHID::generateNewPHID( | return PhabricatorPHID::generateNewPHID( | ||||
PhabricatorPeopleUserPHIDType::TYPECONST); | PhabricatorPeopleUserPHIDType::TYPECONST); | ||||
} | } | ||||
public function hasPassword() { | |||||
return (bool)strlen($this->passwordHash); | |||||
} | |||||
public function setPassword(PhutilOpaqueEnvelope $envelope) { | |||||
if (!$this->getPHID()) { | |||||
throw new Exception( | |||||
pht( | |||||
'You can not set a password for an unsaved user because their PHID '. | |||||
'is a salt component in the password hash.')); | |||||
} | |||||
if (!strlen($envelope->openEnvelope())) { | |||||
$this->setPasswordHash(''); | |||||
} else { | |||||
$this->setPasswordSalt(md5(Filesystem::readRandomBytes(32))); | |||||
$hash = $this->hashPassword($envelope); | |||||
$this->setPasswordHash($hash->openEnvelope()); | |||||
} | |||||
return $this; | |||||
} | |||||
public function getMonogram() { | public function getMonogram() { | ||||
return '@'.$this->getUsername(); | return '@'.$this->getUsername(); | ||||
} | } | ||||
public function isLoggedIn() { | public function isLoggedIn() { | ||||
return !($this->getPHID() === null); | return !($this->getPHID() === null); | ||||
} | } | ||||
Show All 31 Lines | final class PhabricatorUser | ||||
public function hasSession() { | public function hasSession() { | ||||
return ($this->session !== self::ATTACHABLE); | return ($this->session !== self::ATTACHABLE); | ||||
} | } | ||||
private function generateConduitCertificate() { | private function generateConduitCertificate() { | ||||
return Filesystem::readRandomCharacters(255); | return Filesystem::readRandomCharacters(255); | ||||
} | } | ||||
public function comparePassword(PhutilOpaqueEnvelope $envelope) { | |||||
if (!strlen($envelope->openEnvelope())) { | |||||
return false; | |||||
} | |||||
if (!strlen($this->getPasswordHash())) { | |||||
return false; | |||||
} | |||||
return PhabricatorPasswordHasher::comparePassword( | |||||
$this->getPasswordHashInput($envelope), | |||||
new PhutilOpaqueEnvelope($this->getPasswordHash())); | |||||
} | |||||
private function getPasswordHashInput(PhutilOpaqueEnvelope $password) { | |||||
$input = | |||||
$this->getUsername(). | |||||
$password->openEnvelope(). | |||||
$this->getPHID(). | |||||
$this->getPasswordSalt(); | |||||
return new PhutilOpaqueEnvelope($input); | |||||
} | |||||
private function hashPassword(PhutilOpaqueEnvelope $password) { | |||||
$hasher = PhabricatorPasswordHasher::getBestHasher(); | |||||
$input_envelope = $this->getPasswordHashInput($password); | |||||
return $hasher->getPasswordHashForStorage($input_envelope); | |||||
} | |||||
const CSRF_CYCLE_FREQUENCY = 3600; | const CSRF_CYCLE_FREQUENCY = 3600; | ||||
const CSRF_SALT_LENGTH = 8; | const CSRF_SALT_LENGTH = 8; | ||||
const CSRF_TOKEN_LENGTH = 16; | const CSRF_TOKEN_LENGTH = 16; | ||||
const CSRF_BREACH_PREFIX = 'B@'; | const CSRF_BREACH_PREFIX = 'B@'; | ||||
const EMAIL_CYCLE_FREQUENCY = 86400; | const EMAIL_CYCLE_FREQUENCY = 86400; | ||||
const EMAIL_TOKEN_LENGTH = 24; | const EMAIL_TOKEN_LENGTH = 24; | ||||
▲ Show 20 Lines • Show All 1,291 Lines • ▼ Show 20 Lines | switch ($password->getPasswordType()) { | ||||
// originally used this as a hasher, but it became a digest alorithm | // originally used this as a hasher, but it became a digest alorithm | ||||
// once hashing was upgraded to include bcrypt. | // once hashing was upgraded to include bcrypt. | ||||
$digest = $envelope->openEnvelope(); | $digest = $envelope->openEnvelope(); | ||||
$salt = $this->getPHID(); | $salt = $this->getPHID(); | ||||
for ($ii = 0; $ii < 1000; $ii++) { | for ($ii = 0; $ii < 1000; $ii++) { | ||||
$digest = PhabricatorHash::weakDigest($digest, $salt); | $digest = PhabricatorHash::weakDigest($digest, $salt); | ||||
} | } | ||||
return new PhutilOpaqueEnvelope($digest); | return new PhutilOpaqueEnvelope($digest); | ||||
case PhabricatorAuthPassword::PASSWORD_TYPE_ACCOUNT: | |||||
// Account passwords use this weird mess of salt and do not digest | |||||
// the input to a standard length. | |||||
// TODO: We should build a migration pathway forward from this which | |||||
// uses a better (HMAC SHA256) digest algorithm. Beyond this being | |||||
// a weird special case, there are two actual problems with this, | |||||
// although neither are particularly severe: | |||||
// First, because we do not normalize the length of passwords, this | |||||
// algorithm may make us vulnerable to DOS attacks where attacker | |||||
// attempt to use very long inputs to slow down hashers. | |||||
// Second, because the username is part of the hash algorithm, renaming | |||||
// a user breaks their password. This isn't a huge deal but it's pretty | |||||
// silly. There's no security justification for this behavior, I just | |||||
// didn't think about the implication when I wrote it originally. | |||||
$parts = array( | |||||
$this->getUsername(), | |||||
$envelope->openEnvelope(), | |||||
$this->getPHID(), | |||||
$password->getPasswordSalt(), | |||||
); | |||||
return new PhutilOpaqueEnvelope(implode('', $parts)); | |||||
} | } | ||||
// For passwords which do not have some crazy legacy reason to use some | // For passwords which do not have some crazy legacy reason to use some | ||||
// other digest algorithm, HMAC SHA256 is an excellent choice. It satisfies | // other digest algorithm, HMAC SHA256 is an excellent choice. It satisfies | ||||
// the digest requirements and is simple. | // the digest requirements and is simple. | ||||
$digest = PhabricatorHash::digestHMACSHA256( | $digest = PhabricatorHash::digestHMACSHA256( | ||||
$envelope->openEnvelope(), | $envelope->openEnvelope(), | ||||
$password->getPasswordSalt()); | $password->getPasswordSalt()); | ||||
return new PhutilOpaqueEnvelope($digest); | return new PhutilOpaqueEnvelope($digest); | ||||
} | } | ||||
} | } |