Script Valley
REST API Development: Beginner to Production
Authentication and AuthorizationLesson 4.5

Securing passwords — bcrypt hashing and common mistakes

bcrypt algorithm, salt rounds, genSalt, hash, compare, timing attacks, never store plaintext, never log passwords, rate limiting login

Secure Password Storage with bcrypt

Passwords must never be stored in plaintext or using fast hash functions like MD5 or SHA-256. bcrypt is slow by design — it makes brute-force attacks computationally expensive.

Hashing on Registration

const bcrypt = require('bcrypt');

// Registration
const registerUser = async (req, res, next) => {
  try {
    const { email, password } = req.body;

    // Cost factor 12 = ~300ms on modern hardware. Adjust to keep under 1s.
    const passwordHash = await bcrypt.hash(password, 12);

    const user = await User.create({ email, passwordHash });
    res.status(201).json({ id: user.id, email: user.email });
  } catch (err) { next(err); }
};

Verifying on Login

const match = await bcrypt.compare(candidatePassword, storedHash);
// bcrypt.compare is constant-time — safe against timing attacks
// Returns true/false, never throws on mismatch

Common Mistakes

Never log req.body wholesale — it may contain passwords. Never return the passwordHash field in API responses. Never compare passwords with === — use bcrypt.compare which is constant-time. Rate-limit /auth/login to slow credential stuffing: install express-rate-limit and allow maximum 5 attempts per IP per 15 minutes.

const rateLimit = require('express-rate-limit');
app.use('/auth/login', rateLimit({ windowMs: 15*60*1000, max: 5 }));