Predictable recovery tokens (generated with Math.random, timestamps, or sequential IDs) can be brute-forced or guessed, giving attackers account recovery capability without the victim's involvement. CWE-640 (Weak Password Recovery) and CWE-338 (Use of Cryptographically Weak PRNG) both apply. Disclosing account email or username on the reset confirmation page before the token is validated reveals account existence and PII to anyone with a guess-and-click workflow. OWASP A07 covers both weak token generation and information disclosure during recovery as authentication failures.
Medium because successful exploitation requires either guessing a valid token or having prior access to a reset link, but weak random generation significantly reduces the guessing effort.
Use crypto.randomBytes for all recovery tokens — this is the only acceptable random source for security-sensitive values:
// Correct: 256 bits of cryptographic randomness
const token = crypto.randomBytes(32).toString('hex')
// Wrong — do not use either of these:
const token = Date.now().toString() // Predictable
const token = Math.random().toString(36).slice(2) // Not cryptographically secure
On the reset confirmation page, show only a generic 'Enter your new password' form — do not display the account email, name, or any other identifying information before the token is validated and the new password is submitted. Confirm success with a generic message, then redirect to login.
ID: saas-authentication.password-credential.account-recovery-no-leak
Severity: medium
What to look for: Examine the account recovery flow (password reset, "unlock account" flow). Check the response from the reset request endpoint for data that reveals whether an account exists (covered separately in auth-errors-no-email-reveal). Also check the reset confirmation page: after following a reset link, does the page reveal the account email address or other personal details before authentication is complete? Check for token uniqueness — are tokens cryptographically random or predictable? Count all instances found and enumerate each.
Pass criteria: Recovery tokens are cryptographically random (using crypto.randomBytes or equivalent). The recovery flow does not reveal account email, name, or other PII before authentication is complete. Tokens are not sequential or guessable. At least 1 implementation must be confirmed.
Fail criteria: Recovery tokens are predictable (based on timestamp, incremental ID, or weak random number generator). The reset page displays account information (email, name) before the user proves they control the account.
Skip (N/A) when: No account recovery flow. Signal: no "forgot password" or account unlock endpoint.
Detail on fail: "Password reset token generated as Math.random().toString(36) — predictable and not cryptographically secure" or "Reset page displays account email address before the token is validated — confirms account existence".
Remediation: Predictable tokens can be guessed or brute-forced; information disclosed before authentication helps attackers:
// Correct: cryptographically random token
const token = crypto.randomBytes(32).toString('hex') // 256-bit random
// Wrong:
const token = Date.now().toString() // Predictable
const token = Math.random().toString(36).slice(2) // Not cryptographically secure
On the reset confirmation page, show a generic "Enter your new password" form without displaying the email address. Confirm success with a generic message, then redirect to login.