SecureBlockLog inStart a pentest
Vulnerability Repository
MediumAuthentication

Account Enumeration

Differing application responses to valid versus invalid usernames reveal whether accounts exist, enabling targeted attacks and undermining privacy protections.

CVSS 5.3CWE CWE-204OWASP A07:2021 — Identification and Authentication Failures

Description

Account enumeration (CWE-204) occurs when an application returns different responses for valid versus invalid usernames or email addresses, allowing an attacker to determine whether a specific account exists on the platform. This information is a valuable prerequisite for targeted attacks: credential stuffing (testing breach database passwords against confirmed accounts), spear phishing (confirming that a target uses the service before crafting a targeted attack), password spraying, and social engineering.

The vulnerability appears across multiple application flows: login forms, password reset endpoints, registration forms, and account lookup APIs. It falls under OWASP A07:2021 (Identification and Authentication Failures) because it represents a failure to maintain consistent, non-revealing responses across the authentication surface.

Observable differences that leak account existence include: distinct error messages ("Invalid username" vs "Invalid password"), different HTTP response status codes (200 vs 403), different response body lengths, different response times (a valid account may trigger a slower password hash comparison), and different redirect behavior. Even subtle timing differences of a few milliseconds can be reliably exploited with sufficient statistical sampling.

How It Works

Login form enumeration:

POST /login username=existing@corp.com&password=wrong
Response: HTTP 200 — "Incorrect password"  ← Account exists

POST /login username=nonexistent@corp.com&password=wrong
Response: HTTP 200 — "No account found with this email"  ← Account doesn't exist

Password reset enumeration:

POST /forgot-password email=existing@corp.com
Response: "Password reset email sent"

POST /forgot-password email=fake@corp.com
Response: "No account with that email address"

Even if both return the message "If this email exists, you will receive a reset link", timing differences may still reveal existence:

# Measure response times with curl
time curl -X POST /forgot-password -d 'email=existing@corp.com'
# real: 0m0.543s  (email lookup + send)

time curl -X POST /forgot-password -d 'email=fake@corp.com'
# real: 0m0.021s  (early exit, no email send)

A 500ms vs 21ms difference is detectable even under network jitter when averaged over multiple requests.

Registration form enumeration:

POST /register email=existing@corp.com
Response: "This email is already registered. Please log in."

POST /register email=new@corp.com
Response: "Please check your email to confirm your account"

Tools used in penetration testing: Burp Suite Intruder with a username wordlist automates enumeration, comparing response lengths and status codes. ffuf is also commonly used:

ffuf -u https://target.com/forgot-password -X POST \
  -d 'email=FUZZ' -w emails_wordlist.txt \
  -fr "If this email exists"  # Filter out non-matching responses

Impact

  • Targeted Credential Attack — Confirming valid accounts before launching credential stuffing or password spraying attacks, dramatically increasing efficiency
  • Privacy Violation — Revealing that a specific individual has an account on a sensitive platform (healthcare, mental health, adult content, financial)
  • Spear Phishing Enablement — Confirming a target uses the service provides social engineering pretext ("Your account security alert")
  • Username Harvesting — Building a list of valid usernames for subsequent attacks

Detection

  1. Test login with valid vs invalid username — submit known valid and known invalid usernames with the same wrong password. Compare response bodies, status codes, response times, and headers.
  2. Test password reset with valid vs invalid email — submit both to the forgot-password endpoint. Compare all observable response characteristics.
  3. Test registration with existing vs new email — attempt to register with an existing account email and a new address. Compare responses.
  4. Measure timing differences — use Burp Suite Repeater (or a script) to measure average response times for valid vs invalid accounts over 20+ requests each. A statistically significant difference indicates timing-based enumeration.
  5. Test API endpoints — check /api/users/exists?email=, /api/check-username, and similar lookup endpoints that explicitly return account existence.
  6. Test OAuth/SSO flows — some SSO implementations reveal account existence through the error message when an unknown email is submitted to the IdP.

Remediation

Use generic, non-revealing error messages. For login: "Incorrect username or password." For password reset: "If an account exists with this email, you will receive a reset link shortly." Never distinguish between "username not found" and "wrong password".

Implement constant-time responses. Always perform the same amount of work regardless of whether the account exists. For password reset, always execute the email lookup and always wait for it to complete, even if no email is sent:

def forgot_password(email):
    user = User.objects.filter(email=email).first()
    if user:
        token = generate_reset_token()
        send_reset_email(user.email, token)
    # Always sleep to equalize response time
    time.sleep(constant_time_delay)
    return generic_response()

Rate-limit all authentication endpoints. Apply per-IP rate limiting (e.g., 10 requests per minute) to login, registration, and password reset endpoints to raise the cost of systematic enumeration.

Implement CAPTCHA for high-volume enumeration. Add a CAPTCHA challenge after a small number of failed attempts to prevent automated enumeration at scale.

Monitor for enumeration patterns. Log and alert on rapid sequences of failed login attempts or password reset requests from the same IP, even if each individual request appears normal.

Ready when you are
Scope a pentest in the next two minutes.
Start scoping