🛡️ Application Security CheatSheet

Password Reset & Account Recovery

Password reset & account recovery helps users regain access—but it’s also a common account takeover path if implemented weakly.

Beginner mental hook: Recovery must be as strong as login. If not, attackers stop attacking login and attack recovery.

Mental model

  1. Request: user asks to reset (system should not reveal whether the account exists).
  2. Proof: system sends a short-lived, single-use reset token/OTP to a verified channel.
  3. Consume: user redeems token once to set new password.
  4. Invalidate: after reset, old sessions/refresh tokens should be revoked.

Practical note: these issues usually slip through because everything looks “fine” in happy-path testing — until one weird request hits production.

Common failure modes (what goes wrong)

A) Account enumeration

B) Reset token lifecycle bugs

C) Weak verification channel

D) No post-reset revocation

Defensive patterns (Node.js)

Issue a reset token (store hash, short TTL, uniform response)

import crypto from "crypto";

app.post("/password/reset/request", async (req, res) => {
  const email = String(req.body.email || "").toLowerCase();
  const user = await db.users.findByEmail(email);

  // âś… Uniform response to prevent enumeration
  if (!user) return res.json({ ok: true });

  const raw = crypto.randomBytes(32).toString("hex");          // raw token (send to user)
  const hash = crypto.createHash("sha256").update(raw).digest("hex");

  await db.resetTokens.insert({
    userId: user.id,
    tokenHash: hash,
    expiresAt: Date.now() + 15 * 60 * 1000, // 15m
    usedAt: null,
  });

  await emailer.sendResetLink(user.email, raw); // send raw token
  res.json({ ok: true });
});

Consume a reset token (single-use + revoke sessions)

app.post("/password/reset/confirm", async (req, res) => {
  const raw = String(req.body.token || "");
  const newPassword = String(req.body.newPassword || "");

  const hash = crypto.createHash("sha256").update(raw).digest("hex");
  const token = await db.resetTokens.findValidByHash(hash); // checks expiresAt and usedAt

  if (!token) return res.status(400).json({ ok: false, error: "invalid or expired" });

  await db.resetTokens.markUsed(token.id);
  await db.users.updatePassword(token.userId, newPassword);

  // âś… Revoke sessions/refresh tokens after reset
  await db.sessions.revokeAllForUser(token.userId);

  res.json({ ok: true });
});
Guardrails: short TTL, hash at rest, single-use, uniform responses, and post-reset revocation.

Safe validation (defensive verification)

Interview Questions & Answers (Easy → Hard)

Easy

  1. Why is password reset risky?
    A: Plain: it’s a shortcut into accounts. Deep: weak lifecycle becomes primary takeover path.
  2. How do you prevent account enumeration?
    A: Plain: uniform responses. Deep: consistent timing + rate limits + generic messages.
  3. Should you store reset tokens?
    A: Plain: not raw. Deep: store hashed tokens; show raw token once.
  4. How long should reset tokens live?
    A: Plain: short. Deep: minutes with one-time use and invalidation on password change.
  5. What happens after reset?
    A: Plain: user gets back in. Deep: revoke sessions/refresh tokens and notify user.
  6. OTP vs link tokens?
    A: Plain: both can work. Deep: both need short TTL, rate limits, and strong verification.
  7. What do you log?
    A: Plain: events. Deep: requests/confirmations, IP/device metadata; never log tokens.

Medium

  1. Scenario: token can be reused. Impact?
    A: Plain: repeated takeover. Deep: single-use marking required + invalidate on password change.
  2. Scenario: reset doesn’t revoke sessions. Impact?
    A: Plain: attacker stays logged in. Deep: revoke all sessions/refresh tokens after reset.
  3. Scenario: reset page leaks token via referrer/analytics. Fix?
    A: Plain: don’t leak secrets. Deep: avoid tokens in URLs when possible; scrub referrers; strict CSP; avoid logging.
  4. Follow-up: rate limiting strategy?
    A: Plain: slow attackers. Deep: per IP + per account + global thresholds + captcha/risk signals.
  5. Scenario: email account compromised. How do you respond?
    A: Plain: protect recovery. Deep: step-up, delays, and alternate verified channels for high-value accounts.
  6. Follow-up: do you notify users?
    A: Plain: yes. Deep: notify on request and completion; include “not you?” action steps.
  7. Scenario: multiple reset requests spam. What do you do?
    A: Plain: throttle. Deep: cooldown windows, lockouts, and anomaly detection.

Hard

  1. How do you design high-assurance recovery for VIP accounts?
    A: Plain: stronger proof. Deep: step-up, delays, device checks, and manual review with audit trails.
  2. How do you bind reset token to account state?
    A: Plain: invalidate on changes. Deep: include a passwordVersion/securityStamp and reject older tokens after change.
  3. What telemetry matters most?
    A: Plain: watch abuse. Deep: request spikes, geo anomalies, repeated confirmations, and recovery success rates.
  4. Scenario: phishing targets reset flow. Controls?
    A: Plain: reduce success. Deep: short TTL, step-up, user education, and anomaly detection.
  5. How do you test the reset flow safely?
    A: Plain: automated tests. Deep: token reuse/expiry tests, session revocation tests, enumeration tests.
  6. Follow-up: what’s a “security stamp” approach?
    A: Plain: versioning. Deep: bump a server-side value on password reset to invalidate sessions and old tokens.

Exploitation progression (attacker mindset)

Checklist

Remediation playbook

  1. Fix enumeration: uniform responses and consistent timing; add throttling.
  2. Fix token lifecycle: short TTL, single-use, hashed storage, invalidate on reset.
  3. Revoke sessions/refresh tokens post-reset; add alerts for security events.
  4. Harden recovery: additional verification for high-risk users; delays and device checks.
  5. Add tests for token reuse, expiry, and revocation; monitor abuse patterns.