awesome-everything RU
↑ Back to the climb

Security

Password hashing: code review

Crux Read real auth snippets — Argon2id parameters, a naive SHA-256 store, a timing-unsafe compare, and a missing rehash-on-login — and pick the fix a senior would make first.
Your altitude — climbing toward senior
ZeroJuniorMiddleSenior
You are at senior altitude — in orbit
◷ 14 min

Password bugs hide in code that looks fine and passes its happy-path test. Read each snippet, find the defect a breach or a load test would expose, and choose the fix a senior engineer reaches for first.

Goal

Practise the review loop: read an auth path, locate the cryptographic or timing defect, and pick the highest-leverage correction before shipping it to production.

Snippet 1 — the naive store

import { createHash } from "node:crypto";

function storePassword(user, password) {
  const hash = createHash("sha256").update(password).digest("hex");
  db.users.update(user.id, { passwordHash: hash });
}
Quiz

What is wrong with this, and what is the single highest-leverage fix?

Snippet 2 — the Argon2id parameters

import argon2 from "argon2";

async function hashPassword(password) {
  return argon2.hash(password, {
    type: argon2.argon2id,
    memoryCost: 512,   // KiB
    timeCost: 1,
    parallelism: 1,
  });
}
Quiz

The algorithm choice is right. What's wrong with the tuning?

Snippet 3 — the comparison

function verifyResetToken(provided, stored) {
  // both are hex-encoded HMAC digests
  if (provided === stored) {
    return true;
  }
  return false;
}
Quiz

This compares two secret digests with ===. Why is that a problem and what's the fix?

Snippet 4 — login without migration

async function login(email, password) {
  const user = await db.users.findByEmail(email);
  if (!user) return null;
  const ok = await argon2.verify(user.passwordHash, password);
  if (!ok) return null;
  return issueSession(user);
}
Quiz

The verify is correct and constant-time. What does this login miss that matters as parameters age?

Recap

These are the defects you find in real auth reviews: a fast hash with no salt must become a slow, memory-hard KDF; Argon2id with under-sized memory cost throws away the property that defeats GPUs, so honor OWASP’s m=19 MiB/t=2/p=1; secret comparisons must be constant-time (crypto.timingSafeEqual, or the KDF’s own verify) rather than ===; and a correct verify still needs rehash-on-login via needsRehash to keep the work factor current without forced resets. Diagnose the algorithm, the parameters, the timing, and the migration — in that order.

Continue the climb ↑Password hashing: build a migration-safe auth store
shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.