All vulnerabilities
MediumA07:2021CWE-204Authentication

Account Enumeration

Account enumeration lets attackers discover which email addresses or usernames are registered by exploiting different server responses for valid vs. invalid accounts — enabling targeted phishing, credential stuffing, and brute-force attacks.

What Is Account Enumeration?

Account enumeration occurs when an application reveals whether a specific email or username exists in its database through different responses to valid vs. invalid account queries. This seemingly minor information leak enables targeted attacks — attackers build verified lists of real users before launching phishing campaigns or credential stuffing attacks.

How It Works

Response Body Difference

POST /forgot-password
{"email": "real-user@example.com"}
→ 200: "Check your email for a reset link"

POST /forgot-password
{"email": "nobody@example.com"}
→ 200: "No account found with that email"   ← reveals non-existence

An attacker iterates through a list of emails and identifies which ones have accounts.

HTTP Status Code Difference

POST /login
{"email": "real@example.com", "password": "wrong"}
→ 401 Unauthorized

POST /login
{"email": "fake@example.com", "password": "wrong"}
→ 404 Not Found    ← different status reveals account doesn't exist

Timing Oracle

Even with identical response bodies, the server may take longer to process valid accounts (hashing the password, querying the database, sending an email):

POST /login {"email": "real@example.com", "password": "wrong"}
→ Response time: 320ms  (database hit, bcrypt hash comparison)

POST /login {"email": "nobody@example.com", "password": "wrong"}
→ Response time: 12ms   (early exit — user not found)

Measuring response time across many requests reveals which accounts exist.

Registration Flow

POST /register {"email": "real@example.com"}
→ 409 Conflict: "Email already registered"   ← explicit confirmation

POST /register {"email": "new@example.com"}
→ 201 Created

Real-World Impact

  • Targeted phishing — verified email lists sent convincing spear-phishing emails
  • Credential stuffing — confirmed accounts are tested with breached password lists
  • Account takeover — combined with password reset flow, enables targeted attacks
  • Privacy violation — reveals that a person uses a sensitive service (healthcare, dating, finance)

How to Fix

Use consistent response messages for all cases:

# Wrong — reveals whether account exists
@router.post("/forgot-password")
async def forgot_password(email: str):
    user = db.get_user_by_email(email)
    if not user:
        return {"message": "No account found"}  # reveals non-existence
    send_reset_email(user)
    return {"message": "Reset email sent"}
 
# Right — same response always
@router.post("/forgot-password")
async def forgot_password(email: str):
    user = db.get_user_by_email(email)
    if user:
        send_reset_email(user)
    # Always return the same message
    return {"message": "If an account exists, a reset email has been sent"}

Prevent timing oracles — always run the same operations:

import bcrypt
 
def verify_login(email: str, password: str) -> bool:
    user = db.get_user_by_email(email)
    # Always run bcrypt comparison — even for non-existent users
    stored_hash = user.password_hash if user else "$2b$12$invalidhashfortiming"
    is_valid = bcrypt.checkpw(password.encode(), stored_hash.encode())
    return bool(user and is_valid)

Use identical HTTP status codes:

# Always return 401 for login failures — never 404
return JSONResponse({"error": "Invalid credentials"}, status_code=401)

Rate-limit and add CAPTCHA to auth endpoints:

@router.post("/login")
@rate_limit("10/minute")
async def login(data: LoginRequest):
    ...

What VibeWShield Detects

VibeWShield's Account Enumeration scanner targets login, password reset, registration, and recovery endpoints. It compares responses for admin@example.com (likely to exist) vs. a guaranteed non-existent address across three detection methods:

  1. Status code difference — different HTTP status for valid vs. invalid
  2. Response body difference — different text content (e.g., "not found" only for invalid)
  3. Timing oracle — >300ms average delta across 3 samples

Findings are flagged as Medium with the detection method and measured difference as evidence.

#account-enumeration#information-disclosure#authentication#timing-oracle

Free security scan

Test your app for Account Enumeration

VibeWShield automatically checks for Account Enumeration and 40+ other vulnerabilities using 63 scanners — in under 3 minutes, no signup required.

Scan your app free