UntrustedForwardedClientAddress
warning native off by defaultGroup: Probable bugs
Detect HTTP_X_FORWARDED_FOR-derived address used in an access-control comparison without established trusted-proxy validation.
Example
php
<?php
/** @custos-protected */ function grantAccess() {}
function bad($allowedIp) { if ($_SERVER['HTTP_X_FORWARDED_FOR'] === $allowedIp) { grantAccess(); } }Reported:
- line 3: Validate the trusted proxy boundary before trusting forwarded addresses.
Detection
- Report HTTP_X_FORWARDED_FOR-derived address used in an access-control comparison without established trusted-proxy validation.
- Resolve builtin calls and named arguments before checking the contract. Project-local callees are considered only when their bodies or immutable summaries establish the required facts. The default is disabled; enable only when the stated intent matches the application.
- Require a direct equality condition guarding a call to a project function explicitly annotated
@custos-protected. Unknown intent and additional unmodelled proxy guards suppress the finding. - D5. A condition may validate the proxy boundary when it reads
$_SERVER['REMOTE_ADDR']or contains a method call, a static call, or a function call that does not resolve to a builtin. Such a condition counts as an unmodelled proxy guard when it belongs to anif/elseifchain enclosing the comparison'sifwithin the same function scope, or to an earlierifchain in an enclosing statement list of that scope with at least one arm that never falls through (return, throw, exit, break, continue). - For flow-dependent conditions, require a reachable source-to-use path with no intervening invalidation. Unknown calls, ambiguous aliases, recursion or analysis-budget exhaustion cannot establish definite facts.
Exceptions (no report)
- Configured trusted-proxy resolver summaries, logging-only use, non-security uses and missing source-to-sink proof are excluded.
- The comparison is governed by an unmodelled proxy guard as defined in D5: nested inside a guarded
ifchain, or placed after a guard clause that leaves the statement list. Guards outside the enclosing closure or function, earlier checks whose every arm falls through, and conditions made only of plain variables, builtin calls or other$_SERVERkeys do not suppress the report. - Unresolved targets, incomplete required facts, suppressed findings and PHP versions lacking the referenced API or language feature produce no report. User declarations shadowing builtin names are not builtin contracts.
Fix
No automatic fix: trusted proxy addresses and forwarding-chain policy must be supplied by deployment configuration.
PHP versions
Apply only on PHP versions supporting the referenced APIs and syntax (within PHP 5.3–8.5). Builtin behavior and argument contracts follow the configured PHP version. Extension-specific calls require resolved extension API symbols; do not treat same-named application functions as extension calls.
Options
None. Enable or disable this rule through normal rule configuration.
Configure
In custos.json:
json
{
"rules": {
"UntrustedForwardedClientAddress": {
"enabled": true
}
}
}Suppress
Before the statement or declaration (or the first statement of the file):
php
// @custos-ignore UntrustedForwardedClientAddress