← CodeClarityLab Home
Browse by Category
+ added · updated 7d
← Back to glossary

Account Enumeration

security CWE-203 OWASP A2:2021 CVSS 5.3 PHP 5.0+ Intermediate

Also Known As

user enumeration username enumeration

TL;DR

Differing application responses to valid vs. invalid usernames allow attackers to build a list of registered accounts.

Explanation

Account enumeration happens when an application reveals whether a username exists through different HTTP status codes, response bodies, response times, or error messages (e.g., 'This email is not registered' vs. 'Incorrect password'). Armed with a valid username list, attackers can run targeted credential-stuffing or brute-force attacks. Prevention: use generic error messages ('Invalid credentials'), ensure consistent response times with a dummy password-check for unknown users, and implement rate limiting.

Diagram

sequenceDiagram
    participant ATK as Attacker
    participant APP as Application
    ATK->>APP: POST /login user=alice@ex.com pass=wrong
    APP-->>ATK: Email not found - reveals alice does NOT exist
    ATK->>APP: POST /login user=bob@ex.com pass=wrong
    APP-->>ATK: Wrong password - reveals bob DOES exist
    Note over ATK,APP: Attacker now knows valid accounts
    Note over ATK,APP: Fix: always return same message
    ATK->>APP: POST /login user=bob@ex.com pass=wrong
    APP-->>ATK: Invalid credentials - no account info leaked

Common Misconception

A generic "invalid credentials" message fully prevents enumeration. Timing differences, CAPTCHA behaviour, and password-reset flows can still reveal whether an account exists even when error messages are identical.

Why It Matters

Revealing which usernames exist lets attackers target brute-force and credential-stuffing attacks precisely, turning a failed login into a reconnaissance win.

Common Mistakes

  • Returning 'user not found' vs 'wrong password' — both messages reveal account existence.
  • Different response timing for valid vs invalid usernames leaks the same information as different messages.
  • Password reset forms that say 'no account for that email' rather than a generic confirmation.
  • Registration forms that reveal 'username already taken' without rate-limiting or CAPTCHA.

Avoid When

  • Do not use distinct error messages per failure reason on auth endpoints — merge all failures into one generic response.
  • Avoid varying response timing based on whether an account exists — constant-time comparisons prevent timing oracle attacks.
  • Do not expose account existence through side-channels like redirect destinations or HTTP status codes.

When To Use

  • Understanding this attack is essential when designing login, registration, and password-reset flows.
  • Apply enumeration defences on any endpoint where attacker knowledge of a valid account provides value.

Code Examples

💡 Note
The bad example returns "No account found with that email" — an attacker can confirm which emails are registered; the fix returns the same message for wrong email and wrong password.
✗ Vulnerable
// Different messages reveal whether account exists
if (!\$user) {
    return 'No account found with that email';
}
if (!password_verify(\$pw, \$user->password)) {
    return 'Incorrect password'; // confirms account exists!
}
✓ Fixed
// Same message regardless of failure reason
function login(string \$email, string \$password): ?User {
    \$user = User::where('email', \$email)->first();

    // Always run password_verify — prevents timing-based enumeration too
    \$hash  = \$user?->password ?? '\$argon2id\$v=19\$m=65536,t=4,p=1\$dummy\$dummy';
    \$valid = password_verify(\$password, \$hash);

    if (!\$user || !\$valid) {
        return null; // identical response for both failure cases
    }
    return \$user;
}

Added 15 Mar 2026
Edited 31 Mar 2026
Views 41
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 1 ping W 0 pings T 1 ping F 1 ping S 0 pings S 0 pings M 0 pings T 1 ping W 1 ping T 1 ping F 0 pings S 1 ping S 0 pings M 0 pings T 1 ping W 0 pings T
No pings yet today
Amazonbot 14 Perplexity 10 Google 4 Ahrefs 3 Majestic 2 ChatGPT 1 Unknown AI 1
crawler 34 crawler_json 1
DEV INTEL Tools & Severity
🟡 Medium ⚙ Fix effort: Low
⚡ Quick Fix
Return identical response time and message for 'user not found' and 'wrong password' — use hash_equals() and always run password_verify() even for non-existent users
📦 Applies To
PHP 5.0+ web api
🔗 Prerequisites
🔍 Detection Hints
Login returning 'user not found' vs 'wrong password' separately; password reset revealing whether email exists
Auto-detectable: ✓ Yes semgrep owasp-zap
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Medium ✗ Manual fix Fix: Medium Context: Function Tests: Update
CWE-203 CWE-204

✓ schema.org compliant