VNX-204: Observable Response Discrepancy
Reports login code that answers an unknown user or email ("User not found", "no such user", "Invalid username") with a different message from a wrong password, which lets an attacker enumerate accounts.
| Field | Value |
|---|---|
| Rule ID | VNX-204 |
| Severity | Low |
| SARIF level | note |
| Kind | sast |
| Languages | Go, Java, JavaScript / TypeScript, Python, PHP, Ruby, C# |
| CWE | CWE-204 |
| CAPEC | CAPEC-575 |
| ATT&CK | T1589 |
| CWSS vector | CWSS:1.0/TI:L/AP:A/AL:A/IC:N/FC:LT/RP:N/RL:A/AV:I/AS:N/IN:A/SC:A/BI:L/DI:H/EX:L/EC:N/P:L |
| Tags | account-enumeration authentication observable-response cwe-204 |
| Source | internal/sast/rules/vnx-204.rego |
Overview
VNX-204 maps to CWE-204: Observable Response Discrepancy. When a login form says “User not found” for one input and “Wrong password” for another, it tells an attacker which usernames or email addresses have accounts. That list is the starting point for credential stuffing, password spraying and targeted phishing.
What Gets Flagged
A string literal saying the user, username, account, email or login does not exist, is not found, unknown, invalid or not registered, within 20 lines of and in the same function as a distinct message saying the password is wrong. The user-not-found line is reported.
if user is None:
return jsonify(error="User not found"), 401 # flagged
if not user.check_password(request.form["password"]):
return jsonify(error="Incorrect password"), 401
raise AuthError, "#{email} doesn't exist" unless user # flagged
raise AuthError, "Incorrect Password!" unless user.authenticate(password)
Not flagged: one combined message (“Invalid username or password”), a not-found message with no password message beside it (an admin lookup returning 404), log statements, test code, messages in comments, and messages in different functions. Timing differences between the two branches are covered by VNX-205.
Remediation
Return the same message, status code and response shape for both cases:
if user is None or not user.check_password(request.form["password"]):
return jsonify(error="Invalid username or password"), 401
References
- CWE-204: Observable Response Discrepancy
- OWASP Authentication Cheat Sheet: Authentication and Error Messages
Suppressing this rule
Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):
# nosec vnx-204
// nosec vnx-204
Or record an ignore rule: vulnetix ignore add --rule vnx-204 --reason "...". See Suppressing findings.