Password reset tokens are time-limited and single-use
Why it matters
A non-expiring or reusable password reset token is a persistent account takeover vector: intercept it once (from a forwarded email, a shared device, or a log entry), and you can reset the password at any time in the future. CWE-640 (Weak Password Recovery Mechanism for Forgotten Password) and CWE-262 (Not Using Password Aging) both apply. OWASP ASVS V2.5 requires that recovery tokens be short-lived, high-entropy, and single-use; OWASP A07 (Identification and Authentication Failures) lists broken account recovery as a named failure mode.
Severity rationale
High because a non-expiring or reusable reset token provides account takeover capability to anyone who ever possessed the reset link, even if that access was accidental or historical.
Remediation
Store a hash of the token, set a 1-hour expiry, and mark it used atomically on consumption — all three conditions must hold:
// Generation — store hash, send raw token in the email link
const rawToken = crypto.randomBytes(32).toString('hex')
const tokenHash = crypto.createHash('sha256').update(rawToken).digest('hex')
await db.passwordReset.create({
data: { tokenHash, userId, expiresAt: new Date(Date.now() + 3600_000), usedAt: null }
})
// Consumption — validate, check expiry, mark used in one query
const record = await db.passwordReset.findFirst({
where: { tokenHash, usedAt: null, expiresAt: { gt: new Date() } }
})
if (!record) return error('Invalid or expired token')
await db.passwordReset.update({ where: { id: record.id }, data: { usedAt: new Date() } })
The raw token goes in the email link; the hash goes in the database. A leaked database row does not compromise the reset link.
DetectionPro
The detection method for this pattern is part of AuditBuffet Pro.
The full method (what to look for, pass and fail conditions, and how results get reported) ships inside the Authentication Audit. The pattern definition, severity rationale, and remediation remain openly licensed under CC-BY-4.0.
External references
- cwe · CWE-640 — Weak Password Recovery Mechanism for Forgotten Password
- cwe · CWE-262 — Not Using Password Aging
- owasp:2021 · A07
- soc2:2017 · CC6.1 — Logical access security software, infrastructure, and architectures
Taxons
History
- 2026-04-18·v1.0.0·Initial import from saas-authentication·automated
- 2026-07-06·v1.0.1·Add SOC 2 TSC mapping (CC6.1) from the verified compliance crosswalk register (docs/compliance/).·by soc2-mapping-enrichment-ship-2