Careeroza
Hashing Passwords
Never Store Plain Passwords
Store a slow, salted hash. If the database leaks, attackers still cannot read passwords. The usual choice in NestJS apps is bcrypt (or argon2).
Example
npm i bcrypt
npm i -D @types/bcryptExample output
added 2 packages in 3s
import * as bcrypt from 'bcrypt';
const SALT_ROUNDS = 12; // cost: each +1 doubles the time (~250 ms at 12)
export async function hashPassword(plain: string) {
return bcrypt.hash(plain, SALT_ROUNDS); // "$2b$12$Q9w...": algorithm, cost, salt and hash in one string
}
export async function checkPassword(plain: string, hash: string) {
return bcrypt.compare(plain, hash);
}Salt and Slow Hashing, Runnable
Example
bcrypt is an npm package, so this example uses Node's built-in scrypt, which follows the same ideas: a random salt per user (so equal passwords give different hashes), a deliberately slow function, and a constant-time comparison. The salt is fixed here only to keep the output stable.
import { scryptSync, timingSafeEqual } from 'node:crypto';
function hashPassword(plain: string, salt: string): string {
const hash = scryptSync(plain, salt, 32, { N: 16384 }).toString('hex');
return 'scrypt$' + salt + '$' + hash; // store salt with the hash
}
function checkPassword(plain: string, stored: string): boolean {
const [, salt, hash] = stored.split('$');
const candidate = scryptSync(plain, salt, 32, { N: 16384 });
return timingSafeEqual(candidate, Buffer.from(hash, 'hex'));
}
const asha = hashPassword('Shop@2026', 'a1b2c3d4e5f60718');
const ravi = hashPassword('Shop@2026', '0f1e2d3c4b5a6978'); // same password, different salt
console.log(asha.slice(0, 50) + '...');
console.log(ravi.slice(0, 50) + '...');
console.log('same hash for same password?', asha === ravi);
console.log('correct password:', checkPassword('Shop@2026', asha));
console.log('wrong password: ', checkPassword('shop@2026', asha));Output
scrypt$a1b2c3d4e5f60718$d29f6ec0b82c26322ad7fe764c... scrypt$0f1e2d3c4b5a6978$1c612d0cb710e96849b067836b... same hash for same password? false correct password: true wrong password: false