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:
- Status code difference — different HTTP status for valid vs. invalid
- Response body difference — different text content (e.g., "not found" only for invalid)
- Timing oracle — >300ms average delta across 3 samples
Findings are flagged as Medium with the detection method and measured difference as evidence.
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