Instrucciones de origen · Vista previa de solo lectura
name
wstg-idnt-04
description
Test Account Enumeration
category
identity-management
owasp_id
WSTG-IDNT-04
version
1.0.0
author
cyberstrike-official
tags
["identity","user-enum","roles","wstg","idnt"]
tech_stack
[]
cwe_ids
[]
chains_with
[]
prerequisites
[]
severity_boost
{}
wstg-idnt-04
Test ID
WSTG-IDNT-04
Test Name
Testing for Account Enumeration and Guessable User Account
High-Level Description
Account enumeration occurs when an application reveals whether a username or email exists in the system through different response messages, timing differences, or HTTP status codes. Attackers use this information to compile valid account lists for targeted attacks such as brute-force, credential stuffing, or phishing. This test identifies enumeration vulnerabilities across all authentication-related endpoints.
What to Check
Enumeration Vectors
Login form responses
Registration form responses
Password reset functionality
Username recovery feature
API endpoints
Response timing differences
HTTP status code variations
Response Indicators
Location
Enumeration Sign
Login
"Invalid username" vs "Invalid password"
Registration
"Username already exists"
Password Reset
"Email sent" vs "User not found"
API
Different status codes (404 vs 401)
Timing
Faster response for non-existent users
How to Test
Step 1: Test Login Page Enumeration
# Test with valid username, wrong password
curl -s -X POST "https://target.com/login" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=admin&password=wrongpassword" \
-w "\nTime: %{time_total}s"# Test with invalid username
curl -s -X POST "https://target.com/login" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d \
-w
# Bad - Reveals user existenceifnot user_exists(username):
return"User not found"elifnot check_password(username, password):
return"Incorrect password"# Good - Generic messageifnot authenticate(username, password):
return"Invalid username or password"
2. Consistent Response Times
import time
import secrets
defauthenticate(username, password):
# Start timing
start = time.time()
user = get_user(username)
if user:
# Real password check
result = verify_password(password, user.password_hash)
else:
# Dummy computation to match timing
verify_password(password, get_dummy_hash())
result = False# Ensure minimum response time
elapsed = time.time() - start
if elapsed < 0.5:
time.sleep(0.5 - elapsed + secrets.randbelow(100) / 1000)
return result
3. Rate Limiting
from flask_limiter import Limiter
limiter = Limiter(app, key_func=get_remote_address)
@app.route('/login', methods=['POST'])@limiter.limit("5 per minute")deflogin():
# Login logicpass@app.route('/password-reset', methods=['POST'])@limiter.limit("3 per hour")defpassword_reset():
# Always return same messagereturn jsonify({
"message": "If the email exists, a reset link has been sent"
})