Summary
Response bodies are correctly uniform, but timing is not: unknown/inactive emails return before any scrypt work, while real emails pay the scrypt cost — a reliable oracle for valid-account enumeration on both login and password-reset. password-forgot also skips the rate-limit record for unknown emails.
Affected code
app/features/authentication/server/validate-credentials.server.ts (~L4-23)
app/features/authentication/routes/password-forgot.tsx (~L110-138)
Remediation
- For unknown/inactive users, perform a dummy scrypt comparison against a fixed decoy hash to equalize timing.
- Record the reset rate-limit attempt regardless of user existence.
Severity
Medium.
Source: internal security review (commit 0360e87, 2026-07-21).
Summary
Response bodies are correctly uniform, but timing is not: unknown/inactive emails return before any scrypt work, while real emails pay the scrypt cost — a reliable oracle for valid-account enumeration on both login and password-reset.
password-forgotalso skips the rate-limit record for unknown emails.Affected code
app/features/authentication/server/validate-credentials.server.ts(~L4-23)app/features/authentication/routes/password-forgot.tsx(~L110-138)Remediation
Severity
Medium.
Source: internal security review (commit
0360e87, 2026-07-21).