Skip to content

Commit

Permalink
RequestFactory: fix X-Forwarded-Host mixup with remote host
Browse files Browse the repository at this point in the history
  • Loading branch information
JanTvrdik committed Mar 9, 2023
1 parent 09fc207 commit 1ff390b
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 35 deletions.
46 changes: 21 additions & 25 deletions src/Http/RequestFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -273,26 +273,22 @@ private function getMethod(): string

private function getClient(Url $url): array
{
$remoteAddr = !empty($_SERVER['REMOTE_ADDR'])
? $_SERVER['REMOTE_ADDR']
: null;
$remoteHost = !empty($_SERVER['REMOTE_HOST'])
? $_SERVER['REMOTE_HOST']
: null;
$remoteAddr = !empty($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null;

// use real client address and host if trusted proxy is used
$usingTrustedProxy = $remoteAddr && Arrays::some($this->proxies, fn(string $proxy): bool => Helpers::ipMatch($remoteAddr, $proxy));
if ($usingTrustedProxy) {
empty($_SERVER['HTTP_FORWARDED'])
? $this->useNonstandardProxy($url, $remoteAddr, $remoteHost)
: $this->useForwardedProxy($url, $remoteAddr, $remoteHost);
if ($remoteAddr && Arrays::some($this->proxies, fn(string $proxy): bool => Helpers::ipMatch($remoteAddr, $proxy))) {
$remoteHost = null;
$remoteAddr = empty($_SERVER['HTTP_FORWARDED']) ? $this->useNonstandardProxy($url) : $this->useForwardedProxy($url);

} else {
$remoteHost = !empty($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : null;
}

return [$remoteAddr, $remoteHost];
}


private function useForwardedProxy(Url $url, &$remoteAddr, &$remoteHost): void
private function useForwardedProxy(Url $url): ?string
{
$forwardParams = preg_split('/[,;]/', $_SERVER['HTTP_FORWARDED']);
foreach ($forwardParams as $forwardParam) {
Expand All @@ -311,19 +307,17 @@ private function useForwardedProxy(Url $url, &$remoteAddr, &$remoteHost): void
$host = $proxyParams['host'][0];
$startingDelimiterPosition = strpos($host, '[');
if ($startingDelimiterPosition === false) { //IPv4
$remoteHostArr = explode(':', $host);
$remoteHost = $remoteHostArr[0];
$url->setHost($remoteHost);
if (isset($remoteHostArr[1])) {
$url->setPort((int) $remoteHostArr[1]);
$pair = explode(':', $host);
$url->setHost($pair[0]);
if (isset($pair[1])) {
$url->setPort((int) $pair[1]);
}
} else { //IPv6
$endingDelimiterPosition = strpos($host, ']');
$remoteHost = substr($host, strpos($host, '[') + 1, $endingDelimiterPosition - 1);
$url->setHost($remoteHost);
$remoteHostArr = explode(':', substr($host, $endingDelimiterPosition));
if (isset($remoteHostArr[1])) {
$url->setPort((int) $remoteHostArr[1]);
$url->setHost(substr($host, strpos($host, '[') + 1, $endingDelimiterPosition - 1));
$pair = explode(':', substr($host, $endingDelimiterPosition));
if (isset($pair[1])) {
$url->setPort((int) $pair[1]);
}
}
}
Expand All @@ -332,10 +326,11 @@ private function useForwardedProxy(Url $url, &$remoteAddr, &$remoteHost): void
? $proxyParams['proto'][0]
: 'http';
$url->setScheme(strcasecmp($scheme, 'https') === 0 ? 'https' : 'http');
return $remoteAddr ?? null;
}


private function useNonstandardProxy(Url $url, &$remoteAddr, &$remoteHost): void
private function useNonstandardProxy(Url $url): ?string
{
if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
$url->setScheme(strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0 ? 'https' : 'http');
Expand All @@ -361,10 +356,11 @@ private function useNonstandardProxy(Url $url, &$remoteAddr, &$remoteHost): void
if (isset($xForwardedForRealIpKey) && !empty($_SERVER['HTTP_X_FORWARDED_HOST'])) {
$xForwardedHost = explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']);
if (isset($xForwardedHost[$xForwardedForRealIpKey])) {
$remoteHost = trim($xForwardedHost[$xForwardedForRealIpKey]);
$url->setHost($remoteHost);
$url->setHost(trim($xForwardedHost[$xForwardedForRealIpKey]));
}
}

return $remoteAddr ?? null;
}


Expand Down
8 changes: 4 additions & 4 deletions tests/Http/RequestFactory.proxy.forwarded.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ test('', function () {

$factory->setProxy('127.0.0.1/8');
Assert::same('23.75.45.200', $factory->fromGlobals()->getRemoteAddress());
Assert::same('192.168.0.1', @$factory->fromGlobals()->getRemoteHost()); // deprecated
Assert::null(@$factory->fromGlobals()->getRemoteHost()); // deprecated

$url = $factory->fromGlobals()->getUrl();
Assert::same('http', $url->getScheme());
Expand All @@ -43,7 +43,7 @@ test('', function () {

$factory->setProxy('127.0.0.3');
Assert::same('23.75.45.200', $factory->fromGlobals()->getRemoteAddress());
Assert::same('192.168.0.1', @$factory->fromGlobals()->getRemoteHost()); // deprecated
Assert::null(@$factory->fromGlobals()->getRemoteHost()); // deprecated

$url = $factory->fromGlobals()->getUrl();
Assert::same(8080, $url->getPort());
Expand All @@ -62,7 +62,7 @@ test('', function () {

$factory->setProxy('127.0.0.3');
Assert::same('2001:db8:cafe::17', $factory->fromGlobals()->getRemoteAddress());
Assert::same('2001:db8:cafe::18', @$factory->fromGlobals()->getRemoteHost()); // deprecated
Assert::null(@$factory->fromGlobals()->getRemoteHost()); // deprecated

$url = $factory->fromGlobals()->getUrl();
Assert::same('2001:db8:cafe::18', $url->getHost());
Expand All @@ -79,7 +79,7 @@ test('', function () {

$factory->setProxy('127.0.0.3');
Assert::same('2001:db8:cafe::17', $factory->fromGlobals()->getRemoteAddress());
Assert::same('2001:db8:cafe::18', @$factory->fromGlobals()->getRemoteHost()); // deprecated
Assert::null(@$factory->fromGlobals()->getRemoteHost()); // deprecated

$url = $factory->fromGlobals()->getUrl();
Assert::same(47832, $url->getPort());
Expand Down
11 changes: 5 additions & 6 deletions tests/Http/RequestFactory.proxy.x-forwarded.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ test('', function () {

$factory->setProxy('127.0.0.1/8');
Assert::same('23.75.45.200', $factory->fromGlobals()->getRemoteAddress());
Assert::same('otherhost', @$factory->fromGlobals()->getRemoteHost()); // deprecated
Assert::null(@$factory->fromGlobals()->getRemoteHost()); // deprecated

$url = $factory->fromGlobals()->getUrl();
Assert::same('otherhost', $url->getHost());
Expand All @@ -44,12 +44,11 @@ test('', function () {
$factory = new RequestFactory;
$factory->setProxy('10.0.0.0/24');
Assert::same('172.16.0.1', $factory->fromGlobals()->getRemoteAddress());
Assert::same('real', @$factory->fromGlobals()->getRemoteHost()); // deprecated
Assert::null(@$factory->fromGlobals()->getRemoteHost()); // deprecated
Assert::same('real', $factory->fromGlobals()->getUrl()->getHost());

$factory->setProxy(['10.0.0.1', '10.0.0.2']);
Assert::same('172.16.0.1', $factory->fromGlobals()->getRemoteAddress());
Assert::same('real', @$factory->fromGlobals()->getRemoteHost()); // deprecated

$url = $factory->fromGlobals()->getUrl();
Assert::same('real', $url->getHost());
Assert::null(@$factory->fromGlobals()->getRemoteHost()); // deprecated
Assert::same('real', $factory->fromGlobals()->getUrl()->getHost());
});

0 comments on commit 1ff390b

Please sign in to comment.