Skip to content

Commit

Permalink
Implemented RFC 7239 - "Forwarded HTTP Extension" (#94)
Browse files Browse the repository at this point in the history
  • Loading branch information
patrickkusebauch authored and dg committed Jun 17, 2016
1 parent dff9775 commit 3fd485a
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 20 deletions.
76 changes: 57 additions & 19 deletions src/Http/RequestFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -195,30 +195,68 @@ public function createHttpRequest()
$usingTrustedProxy = $remoteAddr && array_filter($this->proxies, function ($proxy) use ($remoteAddr) {
return Helpers::ipMatch($remoteAddr, $proxy);
});

if ($usingTrustedProxy) {
if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
$url->setScheme(strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0 ? 'https' : 'http');
}
if (!empty($_SERVER['HTTP_FORWARDED'])) {
$forwardParams = preg_split('/[,;]/', $_SERVER['HTTP_FORWARDED']);
foreach ($forwardParams as $forwardParam) {
list($key, $value) = explode('=', $forwardParam, 2) + [1 => NULL];
$proxyParams[strtolower(trim($key))][] = trim($value, " \t\"");
}

if (!empty($_SERVER['HTTP_X_FORWARDED_PORT'])) {
$url->setPort((int) $_SERVER['HTTP_X_FORWARDED_PORT']);
}
if (isset($proxyParams['for'])) {
$address = $proxyParams['for'][0];
if (strpos($address, '[') === FALSE) { //IPv4
$remoteAddr = explode(':', $address)[0];
} else { //IPv6
$remoteAddr = substr($address, 1, strpos($address, ']') - 1);
}
}

if (isset($proxyParams['host']) && count($proxyParams['host']) === 1) {
$host = $proxyParams['host'][0];
$startingDelimiterPosition = strpos($host, '[');
if ($startingDelimiterPosition === FALSE) { //IPv4
$remoteHostArr = explode(':', $host);
$remoteHost = $remoteHostArr[0];
if (isset($remoteHostArr[1])) {
$url->setPort((int) $remoteHostArr[1]);
}
} else { //IPv6
$endingDelimiterPosition = strpos($host, ']');
$remoteHost = substr($host, strpos($host, '[') + 1, $endingDelimiterPosition - 1);
$remoteHostArr = explode(':', substr($host, $endingDelimiterPosition));
if (isset($remoteHostArr[1])) {
$url->setPort((int) $remoteHostArr[1]);
}
}
}

$scheme = (isset($proxyParams['scheme']) && count($proxyParams['scheme']) === 1) ? $proxyParams['scheme'][0] : 'http';
$url->setScheme(strcasecmp($scheme, 'https') === 0 ? 'https' : 'http');
} else {
if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
$url->setScheme(strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0 ? 'https' : 'http');
}

if (!empty($_SERVER['HTTP_X_FORWARDED_PORT'])) {
$url->setPort((int) $_SERVER['HTTP_X_FORWARDED_PORT']);
}

if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$xForwardedForWithoutProxies = array_filter(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']), function ($ip) {
return !array_filter($this->proxies, function ($proxy) use ($ip) {
return Helpers::ipMatch(trim($ip), $proxy);
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$xForwardedForWithoutProxies = array_filter(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']), function ($ip) {
return !array_filter($this->proxies, function ($proxy) use ($ip) {
return Helpers::ipMatch(trim($ip), $proxy);
});
});
});
$remoteAddr = trim(end($xForwardedForWithoutProxies));
$xForwardedForRealIpKey = key($xForwardedForWithoutProxies);
}
$remoteAddr = trim(end($xForwardedForWithoutProxies));
$xForwardedForRealIpKey = key($xForwardedForWithoutProxies);
}

if (isset($xForwardedForRealIpKey) && !empty($_SERVER['HTTP_X_FORWARDED_HOST'])) {
$xForwardedHost = explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']);
if (isset($xForwardedHost[$xForwardedForRealIpKey])) {
$remoteHost = trim($xForwardedHost[$xForwardedForRealIpKey]);
if (isset($xForwardedForRealIpKey) && !empty($_SERVER['HTTP_X_FORWARDED_HOST'])) {
$xForwardedHost = explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']);
if (isset($xForwardedHost[$xForwardedForRealIpKey])) {
$remoteHost = trim($xForwardedHost[$xForwardedForRealIpKey]);
}
}
}
}
Expand Down
95 changes: 95 additions & 0 deletions tests/Http/RequestFactory.proxy.forwarded.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

/**
* Test: Nette\Http\RequestFactory and proxy with "Forwarded" header.
*/

use Nette\Http\RequestFactory;
use Tester\Assert;


require __DIR__ . '/../bootstrap.php';

test(function () {
$_SERVER = [
'REMOTE_ADDR' => '127.0.0.3',
'REMOTE_HOST' => 'localhost',
'HTTP_FORWARDED' => 'for=23.75.45.200;host=192.168.0.1',
];

$factory = new RequestFactory;
$factory->setProxy('127.0.0.1');
Assert::same('127.0.0.3', $factory->createHttpRequest()->getRemoteAddress());
Assert::same('localhost', $factory->createHttpRequest()->getRemoteHost());

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

$url = $factory->createHttpRequest()->getUrl();
Assert::same('http', $url->getScheme());
});

test(function () {
$_SERVER = [
'REMOTE_ADDR' => '127.0.0.3',
'REMOTE_HOST' => 'localhost',
'HTTP_FORWARDED' => 'for=23.75.45.200:8080;host=192.168.0.1:8080',
];

$factory = new RequestFactory;

$factory->setProxy('127.0.0.3');
Assert::same('23.75.45.200', $factory->createHttpRequest()->getRemoteAddress());
Assert::same('192.168.0.1', $factory->createHttpRequest()->getRemoteHost());

$url = $factory->createHttpRequest()->getUrl();
Assert::same(8080, $url->getPort());
});


test(function () {
$_SERVER = [
'REMOTE_ADDR' => '127.0.0.3',
'REMOTE_HOST' => 'localhost',
'HTTP_FORWARDED' => 'for="[2001:db8:cafe::17]";host="[2001:db8:cafe::18]"',
];

$factory = new RequestFactory;

$factory->setProxy('127.0.0.3');
Assert::same('2001:db8:cafe::17', $factory->createHttpRequest()->getRemoteAddress());
Assert::same('2001:db8:cafe::18', $factory->createHttpRequest()->getRemoteHost());
});

test(function () {
$_SERVER = [
'REMOTE_ADDR' => '127.0.0.3',
'REMOTE_HOST' => 'localhost',
'HTTP_FORWARDED' => 'for="[2001:db8:cafe::17]:47831";host="[2001:db8:cafe::18]:47832"',
];

$factory = new RequestFactory;

$factory->setProxy('127.0.0.3');
Assert::same('2001:db8:cafe::17', $factory->createHttpRequest()->getRemoteAddress());
Assert::same('2001:db8:cafe::18', $factory->createHttpRequest()->getRemoteHost());

$url = $factory->createHttpRequest()->getUrl();
Assert::same(47832, $url->getPort());
});


test(function () {
$_SERVER = [
'REMOTE_ADDR' => '127.0.0.3',
'REMOTE_HOST' => 'localhost',
'HTTP_FORWARDED' => 'for="[2001:db8:cafe::17]:47831" ; host="[2001:db8:cafe::18]:47832" ; scheme=https',
];

$factory = new RequestFactory;
$factory->setProxy('127.0.0.3');

$url = $factory->createHttpRequest()->getUrl();
Assert::same('https', $url->getScheme());
});
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php

/**
* Test: Nette\Http\RequestFactory and proxy.
* Test: Nette\Http\RequestFactory and proxy with "X-forwarded" headers.
*/

use Nette\Http\RequestFactory;
Expand Down

0 comments on commit 3fd485a

Please sign in to comment.