Skip to main content

Free 30-min security demo  — We'll scan your real code and show live findings, no commitment Book Now

Offensive360
Academy Account Enumeration
Beginner · 15 min

Account Enumeration

Learn how different error messages reveal valid usernames and how generic responses stop information leakage.

1 Enumeration via Login and Reset Responses

Account enumeration occurs when an application reveals whether a username or email is registered through different responses to valid vs invalid accounts.

Login enumeration:

# Vulnerable: different messages for valid vs invalid username
def login(username, password):
    user = db.find_user(username)
    if not user:
        return "Username not found"  # Reveals: username does NOT exist
    if not verify_password(password, user.password_hash):
        return "Incorrect password"  # Reveals: username DOES exist
    return "Login successful"

Password reset enumeration:

POST /reset-password
Email: [email protected] → Response: "Email address not found"
Email: [email protected]  → Response: "Reset link sent to your email"

An attacker can build a list of valid emails/usernames by automating these requests and observing the response differences. This enables targeted phishing and credential stuffing.

2 Generic Responses

The fix is to return identical responses regardless of whether an account exists. This denies attackers the feedback they need to confirm account validity.

Generic login response:

def login(username, password):
    user = db.find_user(username)
    # Always use constant-time comparison and same error
    if not user or not verify_password(password, user.password_hash if user else DUMMY_HASH):
        return "Invalid username or password"
    return generate_token(user)

# IMPORTANT: verify_password must run even when user is None
# to prevent timing-based enumeration (fast "no user" vs slow "wrong password")
DUMMY_HASH = bcrypt.hashpw(b"dummy", bcrypt.gensalt())

Generic reset response:

def request_password_reset(email):
    user = db.find_user_by_email(email)
    if user:
        token = generate_reset_token()
        send_reset_email(email, token)
    # Always return the same message
    return "If an account exists for this email, a reset link has been sent."

Defense checklist:

  • Same error message for invalid username and invalid password
  • Run password hashing even for non-existent users (prevent timing attacks)
  • Same reset response whether or not email is registered
  • Rate limit enumeration attempts

Knowledge Check

0/3 correct
Q1

How does account enumeration help an attacker preparing a credential stuffing attack?

Q2

Why must password verification run even when no user account is found?

Q3

What is the correct password reset response when the submitted email is not registered?

Code Exercise

Return Generic Login Error

The login function returns different messages for invalid username vs invalid password, enabling enumeration. Fix it to always return the same generic error.

python