Add fallback routines for empty secret cases

Signed-off-by: Julius Härtl <jus@bitgrid.net>
This commit is contained in:
Julius Härtl 2022-03-09 10:52:27 +01:00
parent 0825c3ea34
commit 81f8719cc0
No known key found for this signature in database
GPG key ID: 4C614C6ED2CDE6DF
4 changed files with 41 additions and 5 deletions

View file

@ -185,6 +185,7 @@ class PublicKeyTokenProvider implements IProvider {
$this->cache->clear();
$this->mapper->invalidate($this->hashToken($token));
$this->mapper->invalidate($this->hashTokenWithEmptySecret($token));
}
public function invalidateTokenById(string $uid, int $id) {
@ -301,9 +302,14 @@ class PublicKeyTokenProvider implements IProvider {
try {
return $this->crypto->decrypt($cipherText, $token . $secret);
} catch (\Exception $ex) {
// Delete the invalid token
$this->invalidateToken($token);
throw new InvalidTokenException("Could not decrypt token password: " . $ex->getMessage(), 0, $ex);
// Retry with empty secret as a fallback for instances where the secret might not have been set by accident
try {
return $this->crypto->decrypt($cipherText, $token);
} catch (\Exception $ex2) {
// Delete the invalid token
$this->invalidateToken($token);
throw new InvalidTokenException("Could not decrypt token password: " . $ex->getMessage(), 0, $ex2);
}
}
}
@ -326,6 +332,13 @@ class PublicKeyTokenProvider implements IProvider {
return hash('sha512', $token . $secret);
}
/**
* @depreacted Fallback for instances where the secret might not have been set by accident
*/
private function hashTokenWithEmptySecret(string $token): string {
return hash('sha512', $token);
}
/**
* @throws \RuntimeException when OpenSSL reports a problem
*/

View file

@ -125,6 +125,15 @@ class Crypto implements ICrypto {
if ($password === '') {
$password = $this->config->getSystemValue('secret');
}
try {
return $this->decryptWithoutSecret($authenticatedCiphertext, $password);
} catch (Exception $e) {
// Retry with empty secret as a fallback for instances where the secret might not have been set by accident
return $this->decryptWithoutSecret($authenticatedCiphertext, '');
}
}
private function decryptWithoutSecret(string $authenticatedCiphertext, string $password = ''): string {
$hmacKey = $encryptionKey = $password;
$parts = explode('|', $authenticatedCiphertext);

View file

@ -137,6 +137,15 @@ class Hasher implements IHasher {
return true;
}
// Verify whether it matches a legacy PHPass or SHA1 string
// Retry with empty passwordsalt for cases where it was not set
$hashLength = \strlen($hash);
if (($hashLength === 60 && password_verify($message, $hash)) ||
($hashLength === 40 && hash_equals($hash, sha1($message)))) {
$newHash = $this->hash($message);
return true;
}
return false;
}

View file

@ -84,10 +84,15 @@ class VerificationToken implements IVerificationToken {
try {
$decryptedToken = $this->crypto->decrypt($encryptedToken, $passwordPrefix.$this->config->getSystemValue('secret'));
} catch (\Exception $e) {
$this->throwInvalidTokenException(InvalidTokenException::TOKEN_DECRYPTION_ERROR);
// Retry with empty secret as a fallback for instances where the secret might not have been set by accident
try {
$decryptedToken = $this->crypto->decrypt($encryptedToken, $passwordPrefix);
} catch (\Exception $e2) {
$this->throwInvalidTokenException(InvalidTokenException::TOKEN_DECRYPTION_ERROR);
}
}
$splitToken = explode(':', $decryptedToken ?? '');
$splitToken = explode(':', $decryptedToken);
if (count($splitToken) !== 2) {
$this->throwInvalidTokenException(InvalidTokenException::TOKEN_INVALID_FORMAT);
}