diff --git a/src/Http/RequestFactory.php b/src/Http/RequestFactory.php index 4489cffd..ef148900 100644 --- a/src/Http/RequestFactory.php +++ b/src/Http/RequestFactory.php @@ -185,24 +185,33 @@ public function createHttpRequest() } } + $remoteAddr = !empty($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : NULL; + $remoteHost = !empty($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : NULL; - $remoteAddr = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : NULL; - $remoteHost = isset($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : NULL; + // use real client address and host if trusted proxy is used + $usingTrustedProxy = $remoteAddr && array_filter($this->proxies, function ($proxy) use ($remoteAddr) { + return Helpers::ipMatch($remoteAddr, $proxy); + }); - // proxy - foreach ($this->proxies as $proxy) { - if (Helpers::ipMatch($remoteAddr, $proxy)) { - if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { - $remoteAddr = trim(current(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']))); - } - if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) { - $remoteHost = trim(current(explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']))); - } - break; + 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_X_FORWARDED_PORT'])) { + $url->setPort((int) $_SERVER['HTTP_X_FORWARDED_PORT']); } - } + if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { + $remoteAddr = trim(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0]); + } + + if (!empty($_SERVER['HTTP_X_FORWARDED_HOST'])) { + $remoteHost = trim(explode(',', $_SERVER['HTTP_X_FORWARDED_HOST'])[0]); + } + } + // method, eg. GET, PUT, ... $method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : NULL; if ($method === 'POST' && isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']) && preg_match('#^[A-Z]+\z#', $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']) diff --git a/tests/Http/RequestFactory.port.phpt b/tests/Http/RequestFactory.port.phpt new file mode 100644 index 00000000..9d35c92e --- /dev/null +++ b/tests/Http/RequestFactory.port.phpt @@ -0,0 +1,85 @@ +createHttpRequest()->getUrl()->getPort()); + } + + /** + * @return array + */ + public function providerCreateHttpRequest() + { + return [ + [80, []], + [8080, ['HTTP_HOST' => 'localhost:8080']], + [8080, ['SERVER_NAME' => 'localhost:8080']], + [8080, ['HTTP_HOST' => 'localhost:8080', 'SERVER_PORT' => '666']], + [8080, ['SERVER_NAME' => 'localhost:8080', 'SERVER_PORT' => '666']], + [8080, ['HTTP_HOST' => 'localhost', 'SERVER_PORT' => '8080']], + [8080, ['SERVER_NAME' => 'localhost', 'SERVER_PORT' => '8080']], + + [80, ['HTTP_X_FORWARDED_PORT' => '8080']], + [8080, ['HTTP_HOST' => 'localhost:8080', 'HTTP_X_FORWARDED_PORT' => '666']], + [8080, ['SERVER_NAME' => 'localhost:8080', 'HTTP_X_FORWARDED_PORT' => '666']], + [8080, ['HTTP_HOST' => 'localhost:8080', 'SERVER_PORT' => '80', 'HTTP_X_FORWARDED_PORT' => '666']], + [8080, ['SERVER_NAME' => 'localhost:8080', 'SERVER_PORT' => '80', 'HTTP_X_FORWARDED_PORT' => '666']], + [80, ['HTTP_HOST' => 'localhost', 'HTTP_X_FORWARDED_PORT' => '666']], + [80, ['SERVER_NAME' => 'localhost', 'HTTP_X_FORWARDED_PORT' => '666']], + [8080, ['HTTP_HOST' => 'localhost', 'SERVER_PORT' => '8080', 'HTTP_X_FORWARDED_PORT' => '666']], + [8080, ['SERVER_NAME' => 'localhost', 'SERVER_PORT' => '8080', 'HTTP_X_FORWARDED_PORT' => '666']], + [44443, ['HTTPS' => 'on', 'SERVER_NAME' => 'localhost:44443', 'HTTP_X_FORWARDED_PORT' => '666']], + ]; + } + + /** + * @dataProvider providerCreateHttpRequestWithTrustedProxy + */ + public function testCreateHttpRequestWithTrustedProxy($expectedPort, array $server) + { + $_SERVER = array_merge(['REMOTE_ADDR' => '10.0.0.1'], $server); + + $factory = new Nette\Http\RequestFactory; + $factory->setProxy(['10.0.0.1']); + Assert::same($expectedPort, $factory->createHttpRequest()->getUrl()->getPort()); + } + + /** + * @return array + */ + public function providerCreateHttpRequestWithTrustedProxy() + { + return [ + [8080, ['HTTP_X_FORWARDED_PORT' => '8080']], + [8080, ['HTTP_HOST' => 'localhost:666', 'HTTP_X_FORWARDED_PORT' => '8080']], + [8080, ['SERVER_NAME' => 'localhost:666', 'HTTP_X_FORWARDED_PORT' => '8080']], + [8080, ['HTTP_HOST' => 'localhost:666', 'SERVER_PORT' => '80', 'HTTP_X_FORWARDED_PORT' => '8080']], + [8080, ['SERVER_NAME' => 'localhost:666', 'SERVER_PORT' => '80', 'HTTP_X_FORWARDED_PORT' => '8080']], + [8080, ['HTTP_HOST' => 'localhost', 'HTTP_X_FORWARDED_PORT' => '8080']], + [8080, ['SERVER_NAME' => 'localhost', 'HTTP_X_FORWARDED_PORT' => '8080']], + [8080, ['HTTP_HOST' => 'localhost', 'SERVER_PORT' => '666', 'HTTP_X_FORWARDED_PORT' => '8080']], + [8080, ['SERVER_NAME' => 'localhost', 'SERVER_PORT' => '666', 'HTTP_X_FORWARDED_PORT' => '8080']], + [44443, ['HTTPS' => 'on', 'SERVER_NAME' => 'localhost:666', 'HTTP_X_FORWARDED_PORT' => '44443']], + ]; + } + +} + +$test = new RequestFactoryPortTest(); +$test->run(); diff --git a/tests/Http/RequestFactory.scheme.phpt b/tests/Http/RequestFactory.scheme.phpt new file mode 100644 index 00000000..ef0ce7a5 --- /dev/null +++ b/tests/Http/RequestFactory.scheme.phpt @@ -0,0 +1,79 @@ +createHttpRequest()->getUrl()->getScheme()); + } + + /** + * @return array + */ + public function providerCreateHttpRequest() + { + return [ + ['http', []], + ['http', ['HTTPS' => '']], + ['http', ['HTTPS' => 'off']], + ['http', ['HTTP_X_FORWARDED_PROTO' => 'https']], + ['http', ['HTTP_X_FORWARDED_PORT' => '443']], + ['http', ['HTTP_X_FORWARDED_PROTO' => 'https', 'HTTP_X_FORWARDED_PORT' => '443']], + + ['https', ['HTTPS' => 'on']], + ['https', ['HTTPS' => 'anything']], + ['https', ['HTTPS' => 'on', 'HTTP_X_FORWARDED_PROTO' => 'http']], + ['https', ['HTTPS' => 'on', 'HTTP_X_FORWARDED_PORT' => '80']], + ['https', ['HTTPS' => 'on', 'HTTP_X_FORWARDED_PROTO' => 'http', 'HTTP_X_FORWARDED_PORT' => '80']], + ]; + } + + /** + * @covers RequestFactory::getScheme + * @dataProvider providerCreateHttpRequestWithTrustedProxy + */ + public function testCreateHttpRequestWithTrustedProxy($expectedScheme, array $server) + { + $_SERVER = array_merge(['REMOTE_ADDR' => '10.0.0.1'], $server); + + $factory = new Nette\Http\RequestFactory; + $factory->setProxy(['10.0.0.1']); + Assert::same($expectedScheme, $factory->createHttpRequest()->getUrl()->getScheme()); + } + + /** + * @return array + */ + public function providerCreateHttpRequestWithTrustedProxy() + { + return [ + ['http', ['HTTP_X_FORWARDED_PROTO' => 'http']], + ['http', ['HTTPS' => 'on', 'HTTP_X_FORWARDED_PROTO' => 'http']], + ['http', ['HTTPS' => 'on', 'HTTP_X_FORWARDED_PROTO' => 'something-unexpected']], + ['http', ['HTTPS' => 'on', 'HTTP_X_FORWARDED_PROTO' => 'http', 'HTTP_X_FORWARDED_PORT' => '443']], + + ['https', ['HTTP_X_FORWARDED_PROTO' => 'https']], + ['https', ['HTTPS' => 'off', 'HTTP_X_FORWARDED_PROTO' => 'https']], + ['https', ['HTTPS' => 'off', 'HTTP_X_FORWARDED_PROTO' => 'https', 'HTTP_X_FORWARDED_PORT' => '80']], + ]; + } + +} + +$test = new RequestFactorySchemeTest(); +$test->run();