mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-11-22 09:46:30 -05:00
f30f0d8e51
OWASP [1] recommends for password hashing the following algorithms in descending order: argon2id, scrypt, bcrypt. They state that bcrypt may be used in legacy systems or when required due to legal regulations. We're however not building any legacy application. Even HedgeDoc 1.x utilizes a more modern algorithm by using scrypt. While bcrypt is not insecure per se, our implementation had a major security flaw, leading to invalid passwords being accepted in certain cases. The bcrypt nodejs package - and the OWASP cheatsheet as well - point out, that the maximum input length of passwords is limited to 72 bytes with bcrypt. When some user has a password longer than 72 bytes in use, only the first 72 bytes are required to log in successfully. Depending on the encoding (which could be UTF-8 or UTF-16 depending on different circumstances) this could in worst-case be at 36 characters, which is not very unusual for a password. See also [2]. This commit changes the used algorithm to argon2id. Argon2id has been in use for several years now and seems to be a well-designed password hashing function that even won the 2015 Password Hashing Competition. Argon2 does not have any real-world max input length for passwords (it is at 4 GiB). The node-rs/argon2 implementation seems to be well maintained, widely used (more than 150k downloads per week) and is published with provenance, proving that the npm package was built on GitHub actions using the source code in the repository. The implementation is written in Rust, so it should be safe against memory leakages etc. [1]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Che at_Sheet.html#password-hashing-algorithms [2]: https://security.stackexchange.com/a/39851 Signed-off-by: Erik Michelson <github@erik.michelson.eu>
47 lines
1.3 KiB
TypeScript
47 lines
1.3 KiB
TypeScript
/*
|
|
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
import { hash, verify } from '@node-rs/argon2';
|
|
|
|
/**
|
|
* Hashes a password using argon2id
|
|
*
|
|
* @param cleartext The password to hash
|
|
* @returns The hashed password
|
|
*/
|
|
export async function hashPassword(cleartext: string): Promise<string> {
|
|
// options recommended by OWASP
|
|
// https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id
|
|
return await hash(cleartext, {
|
|
memoryCost: 19456,
|
|
timeCost: 2,
|
|
parallelism: 1,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Checks if a cleartext password matches a password hash
|
|
*
|
|
* @param cleartext The cleartext password
|
|
* @param passwordHash The password hash
|
|
* @returns Whether the password matches the hash
|
|
*/
|
|
export async function checkPassword(
|
|
cleartext: string,
|
|
passwordHash: string,
|
|
): Promise<boolean> {
|
|
return await verify(passwordHash, cleartext);
|
|
}
|
|
|
|
export function bufferToBase64Url(text: Buffer): string {
|
|
// This is necessary as the is no base64url encoding in the toString method
|
|
// but as can be seen on https://tools.ietf.org/html/rfc4648#page-7
|
|
// base64url is quite easy buildable from base64
|
|
return text
|
|
.toString('base64')
|
|
.replace(/\+/g, '-')
|
|
.replace(/\//g, '_')
|
|
.replace(/=+$/, '');
|
|
}
|