VNX-290: Authentication Bypass by Spoofing
Detects trust decisions on a client IP taken from a client-controlled header (X-Forwarded-For, X-Real-IP, True-Client-IP, HTTP_X_FORWARDED_FOR): comparison with an IP literal, allow-list membership, an is_trusted-style helper, an address-class test (is_private, isLoopbackAddress), or a rate limiter keyed on the header.
| Field | Value |
|---|---|
| Rule ID | VNX-290 |
| Severity | High |
| SARIF level | error |
| Kind | sast |
| Languages | Java, JavaScript / TypeScript, Python |
| CWE | CWE-290 |
| CAPEC | CAPEC-66 |
| ATT&CK | T1190 |
| CWSS vector | CWSS:1.0/TI:H/AP:A/AL:A/IC:N/FC:LT/RP:N/RL:A/AV:I/AS:N/IN:A/SC:A/BI:H/DI:H/EX:H/EC:N/P:H |
| Tags | auth spoofing x-forwarded-for cwe-290 |
| Source | internal/sast/rules/vnx-290.rego |
Overview
VNX-290 maps to CWE-290: Authentication Bypass by Spoofing. X-Forwarded-For and its relatives are request headers: the client writes them. Code that grants access when the header says 127.0.0.1, or limits login attempts per header value, can be bypassed by sending a different value.
The rule reports the header value (read inline, or through a variable assigned from it, or derived from such a variable such as xff.split(',')[0], in the 12 lines above) when it is:
- compared with an IP or
localhostliteral (==,===,!=,.equals,.startsWith), - tested for membership (
in ALLOWED,.includes,.contains,.has,.indexOf) or passed to anis_trusted/isInternal/isAllowed-style helper, - or used as a rate-limiter key (
keyGenerator,key_func,keyResolver).
What Gets Flagged
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
if ip == '127.0.0.1': # FLAGGED
return HttpResponse(render_status())
String clientIp = request.getHeader("X-Forwarded-For");
if ("127.0.0.1".equals(clientIp)) { // FLAGGED
keyGenerator ({ headers, ip }) { return headers['X-Forwarded-For'] ?? ip } // FLAGGED
What Is Not Flagged
- Logging or storing the header, and null or empty checks.
- Decisions on the socket address (
request.getRemoteAddr(),REMOTE_ADDR, Expressreq.ipwithtrust proxy). - Setting the header on a response or outgoing request, test code and comments.
Whether a trusted reverse proxy overwrites the header before it reaches this code is deployment configuration and is not judged.
Remediation
Use the socket address, or let the framework take the client address from a known number of trusted proxies (Express app.set('trust proxy', 1), Werkzeug ProxyFix(x_for=1), Spring ForwardedHeaderFilter). Authenticate callers rather than trusting their address, and key rate limits on the socket address or the targeted account.
References
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-290
// nosec vnx-290
Or record an ignore rule: vulnetix ignore add --rule vnx-290 --reason "...". See Suppressing findings.