Password reset endpoint enumerates registered emails
POST /api/auth/password-reset
description
The password reset endpoint returns different responses for registered and unregistered email addresses. Registered emails receive a 200 with body `{"sent": true}`. Unregistered emails receive a 404 with body `{"error": "no such user"}`. The endpoint also has no rate limiting, so an attacker can iterate a list of emails and determine which ones are registered. We confirmed registration status for 12 emails from a public breach corpus in under 30 seconds.
evidence
Request
POST /api/auth/password-reset HTTP/1.1
Host: acme.dev
Content-Type: application/json
{"email":"victim@example.com"}
Response
HTTP/1.1 200 OK
Content-Type: application/json
{"sent": true}
- vs -
HTTP/1.1 404 Not Found
Content-Type: application/json
{"error": "no such user"}
reproduction steps
- POST a registered email to /api/auth/password-reset → observe 200 `{sent: true}`.
- POST an unregistered email → observe 404 `{error: 'no such user'}`.
- Iterate a list of candidate emails to enumerate registered users.
remediation
Return an identical response whether or not the email exists (200 with a constant-time delay); send the mail only when the account is real. Rate limit per IP and per email.
python
user = db.users.find(email)
if user:
send_reset_email(user)
return Response({'sent': True}, status=200) # same body + timing either wayrotate the credentials you provided.
we ran the scan with the credentials you supplied. treat them as compromised and rotate now.
couldn't reproduce this finding?
if it does not reproduce from the evidence above, you get back what this finding cost you: its credits for a credit unlock, or a cash refund for a finding bought through our earlier per-finding checkout. see how claims work, or email team@recon.sh with finding ID f6 and scan ID sample.