Skip to content

Commit

Permalink
fix(authtoken): Store only one hash for authtokens with the current p…
Browse files Browse the repository at this point in the history
…assword per user

Signed-off-by: Julius Härtl <jus@bitgrid.net>
  • Loading branch information
juliushaertl authored and summersab committed Feb 9, 2023
1 parent d71fa03 commit ce10d86
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 1 deletion.
27 changes: 27 additions & 0 deletions lib/private/Authentication/Token/PublicKeyTokenMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -229,4 +229,31 @@ public function updateActivity(IToken $token, int $now): void {
);
$update->executeStatement();
}

public function updateHashesForUser(string $userId, string $passwordHash): void {
$qb = $this->db->getQueryBuilder();
$update = $qb->update($this->getTableName())
->set('password_hash', $qb->createNamedParameter($passwordHash))
->where(
$qb->expr()->eq('uid', $qb->createNamedParameter($userId))
);
$update->executeStatement();
}

public function getFirstTokenForUser(string $userId): ?PublicKeyToken {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('uid', $qb->createNamedParameter($userId)))
->setMaxResults(1)
->orderBy('id');
$result = $qb->executeQuery();

$data = $result->fetch();
$result->closeCursor();
if ($data === false) {
return null;
}
return PublicKeyToken::fromRow($data);
}
}
24 changes: 23 additions & 1 deletion lib/private/Authentication/Token/PublicKeyTokenProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,23 @@ public function generateToken(string $token,
$name = mb_substr($name, 0, 120) . '';
}

// We need to check against one old token to see if there is a password
// hash that we can reuse for detecting outdated passwords
$randomOldToken = $this->mapper->getFirstTokenForUser($uid);
$oldTokenMatches = $randomOldToken && $this->hasher->verify(sha1($password) . $password, $randomOldToken->getPasswordHash());

$dbToken = $this->newToken($token, $uid, $loginName, $password, $name, $type, $remember);

if ($oldTokenMatches) {
$dbToken->setPasswordHash($randomOldToken->getPasswordHash());
}

$this->mapper->insert($dbToken);

if (!$oldTokenMatches && $password !== null) {
$this->updatePasswords($uid, $password);
}

// Add the token to the cache
$this->cache[$dbToken->getToken()] = $dbToken;

Expand Down Expand Up @@ -289,10 +303,11 @@ public function setPassword(IToken $token, string $tokenId, string $password) {

// Update the password for all tokens
$tokens = $this->mapper->getTokenByUser($token->getUID());
$hashedPassword = $this->hashPassword($password);
foreach ($tokens as $t) {
$publicKey = $t->getPublicKey();
$t->setPassword($this->encryptPassword($password, $publicKey));
$t->setPasswordHash($this->hashPassword($password));
$t->setPasswordHash($hashedPassword);
$this->updateToken($t);
}
}
Expand Down Expand Up @@ -481,6 +496,13 @@ public function updatePasswords(string $uid, string $password) {
$this->updateToken($t);
}
}

// If password hashes are different we update them all to be equal so
// that the next execution only needs to verify once
if (count($hashNeedsUpdate) > 1) {
$newPasswordHash = $this->hashPassword($password);
$this->mapper->updateHashesForUser($uid, $newPasswordHash);
}
}

private function logOpensslError() {
Expand Down

0 comments on commit ce10d86

Please sign in to comment.