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/bcrypt

Example 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