What steps will reproduce the problem?
Proxies and load balancers typically send a single X-Forwarded-For header value with a comma-separated chain:
X-Forwarded-For: 9.9.9.9, 5.5.5.5, 2.2.2.2
In PSR-7 that becomes one getHeader('x-forwarded-for') element: "9.9.9.9, 5.5.5.5, 2.2.2.2".
TrustedHostsNetworkResolver::getConnectionChainItems() uses those values as-is and passes each one into getConnectionChainItem() as an IP. The comma-separated string fails IpValidator::isIp() and throws.
Minimal contrast:
- Multiple header values via
withAddedHeader for 9.9.9.9, 5.5.5.5, 2.2.2.2
-> chain resolves (e.g. requestClientIp = 9.9.9.9 with trusted proxies configured).
- One value
withHeader('X-Forwarded-For', '9.9.9.9, 5.5.5.5, 2.2.2.2')
-> InvalidConnectionChainItemException: "9.9.9.9, 5.5.5.5, 2.2.2.2" is not a valid IP.
$middleware = (new TrustedHostsNetworkResolver())
->withTrustedIps(['18.18.18.18', '2.2.2.2', '5.5.5.5'])
->withForwardedHeaderGroups([
TrustedHostsNetworkResolver::FORWARDED_HEADER_GROUP_X_PREFIX,
]);
$request = (new ServerRequest(['REMOTE_ADDR' => '18.18.18.18']))
-> withHeader('X-Forwarded-For', '9.9.9.9, 5.5.5.5, 2.2.2.2');
$middleware->process($request, $handler);
// throws InvalidConnectionChainItemException
What is the expected result?
Comma-separated X-Forwarded-For should be split (and trimmed) into individual IPs before validation / chain walking - matching common proxy behaviour and existing libs (e.g. Symfony / Laravel trusted proxies).
What do you get instead?
The whole comma-separated string is treated as one IP -> validation error.
Additional info
| Q |
A |
| Version |
yiisoft/proxy-middleware 1.0.2 |
| PHP version |
8.3 |
Relevant code
// TrustedHostsNetworkResolver::getConnectionChainItems()
$forwardedHeaderValue = $request->getHeader($forwardedHeaderGroup['ip']);
// ...
$requestIps = array_merge([$remoteAddr], array_reverse($forwardedHeaderValue));
foreach ($requestIps as $requestIp) {
$items[] = $this->getConnectionChainItem(
ip: $requestIp,
// ...
);
}
// TrustedHostsNetworkResolver::getConnectionChainItem()
if ($ip !== null && $validateIp && !IpValidator::isIp($ip)) {
throw new InvalidConnectionChainItemException("\"$ip\" is not a valid IP.");
}
Suggested fix
Before building $requestIps from x-forwarded-for header values, expand each header line:
explode(',', $value)
trim each part
- drop empty segments
then validate / reverse the flattened IP list.
What steps will reproduce the problem?
Proxies and load balancers typically send a single
X-Forwarded-Forheader value with a comma-separated chain:X-Forwarded-For: 9.9.9.9, 5.5.5.5, 2.2.2.2In PSR-7 that becomes one
getHeader('x-forwarded-for')element:"9.9.9.9, 5.5.5.5, 2.2.2.2".TrustedHostsNetworkResolver::getConnectionChainItems()uses those values as-is and passes each one intogetConnectionChainItem()as an IP. The comma-separated string failsIpValidator::isIp()and throws.Minimal contrast:
withAddedHeaderfor9.9.9.9,5.5.5.5,2.2.2.2-> chain resolves (e.g.
requestClientIp = 9.9.9.9with trusted proxies configured).withHeader('X-Forwarded-For', '9.9.9.9, 5.5.5.5, 2.2.2.2')->
InvalidConnectionChainItemException:"9.9.9.9, 5.5.5.5, 2.2.2.2" is not a valid IP.What is the expected result?
Comma-separated
X-Forwarded-Forshould be split (and trimmed) into individual IPs before validation / chain walking - matching common proxy behaviour and existing libs (e.g. Symfony / Laravel trusted proxies).What do you get instead?
The whole comma-separated string is treated as one IP -> validation error.
Additional info
yiisoft/proxy-middleware1.0.2Relevant code
Suggested fix
Before building
$requestIpsfromx-forwarded-forheader values, expand each header line:explode(',', $value)trimeach partthen validate / reverse the flattened IP list.