diff --git a/src/Illuminate/Http/Middleware/TrustProxies.php b/src/Illuminate/Http/Middleware/TrustProxies.php index e9e06968b806..65a38c97960d 100644 --- a/src/Illuminate/Http/Middleware/TrustProxies.php +++ b/src/Illuminate/Http/Middleware/TrustProxies.php @@ -19,7 +19,7 @@ class TrustProxies * * @var int */ - protected $headers = Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO | Request::HEADER_X_FORWARDED_AWS_ELB; + protected $headers = Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PREFIX | Request::HEADER_X_FORWARDED_PROTO | Request::HEADER_X_FORWARDED_AWS_ELB; /** * Handle an incoming request. @@ -113,12 +113,16 @@ protected function getTrustedHeaderNames() case Request::HEADER_X_FORWARDED_PORT: return Request::HEADER_X_FORWARDED_PORT; + case 'HEADER_X_FORWARDED_PREFIX': + case Request::HEADER_X_FORWARDED_PREFIX: + return Request::HEADER_X_FORWARDED_PREFIX; + case 'HEADER_X_FORWARDED_PROTO': case Request::HEADER_X_FORWARDED_PROTO: return Request::HEADER_X_FORWARDED_PROTO; default: - return Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO | Request::HEADER_X_FORWARDED_AWS_ELB; + return Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PREFIX | Request::HEADER_X_FORWARDED_PROTO | Request::HEADER_X_FORWARDED_AWS_ELB; } return $this->headers; diff --git a/tests/Http/Middleware/TrustProxiesTest.php b/tests/Http/Middleware/TrustProxiesTest.php index 6f653d09874d..f595147013c4 100644 --- a/tests/Http/Middleware/TrustProxiesTest.php +++ b/tests/Http/Middleware/TrustProxiesTest.php @@ -13,7 +13,7 @@ class TrustProxiesTest extends TestCase * * @var int */ - protected $headerAll = Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO | Request::HEADER_X_FORWARDED_AWS_ELB; + protected $headerAll = Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PREFIX | Request::HEADER_X_FORWARDED_PROTO | Request::HEADER_X_FORWARDED_AWS_ELB; /** * Test that Symfony does indeed NOT trust X-Forwarded-* @@ -30,6 +30,7 @@ public function test_request_does_not_trust() $this->assertEquals('http', $req->getScheme(), 'Assert untrusted proxy x-forwarded-proto header not used'); $this->assertEquals('localhost', $req->getHost(), 'Assert untrusted proxy x-forwarded-host header not used'); $this->assertEquals(8888, $req->getPort(), 'Assert untrusted proxy x-forwarded-port header not used'); + $this->assertEquals('', $req->getBaseUrl(), 'Assert untrusted proxy x-forwarded-prefix header not used'); } /** @@ -47,6 +48,7 @@ public function test_does_trust_trusted_proxy() $this->assertEquals('https', $req->getScheme(), 'Assert trusted proxy x-forwarded-proto header used'); $this->assertEquals('serversforhackers.com', $req->getHost(), 'Assert trusted proxy x-forwarded-host header used'); $this->assertEquals(443, $req->getPort(), 'Assert trusted proxy x-forwarded-port header used'); + $this->assertEquals('/prefix', $req->getBaseUrl(), 'Assert trusted proxy x-forwarded-prefix header used'); } /** @@ -204,6 +206,7 @@ public function test_x_forwarded_for_header_only_trusted() $this->assertEquals('localhost', $request->getHost(), 'Assert trusted proxy did not use forwarded header for host'); $this->assertEquals(8888, $request->getPort(), 'Assert trusted proxy did not use forwarded header for port'); + $this->assertEquals('', $request->getBaseUrl(), 'Assert trusted proxy did not use forwarded header for prefix'); }); } @@ -224,6 +227,7 @@ public function test_x_forwarded_host_header_only_trusted() $this->assertEquals('serversforhackers.com', $request->getHost(), 'Assert trusted proxy used forwarded header for host'); $this->assertEquals(8888, $request->getPort(), 'Assert trusted proxy did not use forwarded header for port'); + $this->assertEquals('', $request->getBaseUrl(), 'Assert trusted proxy did not use forwarded header for prefix'); }); } @@ -244,6 +248,29 @@ public function test_x_forwarded_port_header_only_trusted() $this->assertEquals('localhost', $request->getHost(), 'Assert trusted proxy did not use forwarded header for host'); $this->assertEquals(443, $request->getPort(), 'Assert trusted proxy used forwarded header for port'); + $this->assertEquals('', $request->getBaseUrl(), 'Assert trusted proxy did not use forwarded header for prefix'); + }); + } + + /** + * Test that only the X-Forwarded-Prefix header is trusted. + */ + public function test_x_forwarded_prefix_header_only_trusted() + { + $trustedProxy = $this->createTrustedProxy(Request::HEADER_X_FORWARDED_PREFIX, '*'); + + $request = $this->createProxiedRequest(); + + $trustedProxy->handle($request, function ($request) { + $this->assertEquals('192.168.10.10', $request->getClientIp(), + 'Assert trusted proxy did not use forwarded header for IP'); + $this->assertEquals('http', $request->getScheme(), + 'Assert trusted proxy did not use forwarded header for scheme'); + $this->assertEquals('localhost', $request->getHost(), + 'Assert trusted proxy did not use forwarded header for host'); + $this->assertEquals(8888, $request->getPort(), 'Assert trusted proxy did not use forwarded header for port'); + $this->assertEquals(8888, $request->getPort(), 'Assert trusted proxy did not use forwarded header for port'); + $this->assertEquals('/prefix', $request->getBaseUrl(), 'Assert trusted proxy used forwarded header for prefix'); }); } @@ -264,6 +291,7 @@ public function test_x_forwarded_proto_header_only_trusted() $this->assertEquals('localhost', $request->getHost(), 'Assert trusted proxy did not use forwarded header for host'); $this->assertEquals(8888, $request->getPort(), 'Assert trusted proxy did not use forwarded header for port'); + $this->assertEquals('', $request->getBaseUrl(), 'Assert trusted proxy did not use forwarded header for prefix'); }); } @@ -274,7 +302,8 @@ public function test_x_forwarded_multiple_individual_headers_trusted() { $trustedProxy = $this->createTrustedProxy( Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | - Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO, + Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PREFIX | + Request::HEADER_X_FORWARDED_PROTO, '*' ); @@ -288,6 +317,7 @@ public function test_x_forwarded_multiple_individual_headers_trusted() $this->assertEquals('serversforhackers.com', $request->getHost(), 'Assert trusted proxy used forwarded header for host'); $this->assertEquals(443, $request->getPort(), 'Assert trusted proxy used forwarded header for port'); + $this->assertEquals('/prefix', $request->getBaseUrl(), 'Assert trusted proxy used forwarded header for prefix'); }); } @@ -340,10 +370,11 @@ protected function createProxiedRequest($serverOverRides = []) // Add some X-Forwarded headers and over-ride // defaults, simulating a request made over a proxy $serverOverRides = array_replace([ - 'HTTP_X_FORWARDED_FOR' => '173.174.200.38', // X-Forwarded-For -- getClientIp() - 'HTTP_X_FORWARDED_HOST' => 'serversforhackers.com', // X-Forwarded-Host -- getHosts() - 'HTTP_X_FORWARDED_PORT' => '443', // X-Forwarded-Port -- getPort() - 'HTTP_X_FORWARDED_PROTO' => 'https', // X-Forwarded-Proto -- getScheme() / isSecure() + 'HTTP_X_FORWARDED_FOR' => '173.174.200.38', // X-Forwarded-For -- getClientIp() + 'HTTP_X_FORWARDED_HOST' => 'serversforhackers.com', // X-Forwarded-Host -- getHosts() + 'HTTP_X_FORWARDED_PORT' => '443', // X-Forwarded-Port -- getPort() + 'HTTP_X_FORWARDED_PREFIX' => '/prefix', // X-Forwarded-Prefix -- getBaseUrl() + 'HTTP_X_FORWARDED_PROTO' => 'https', // X-Forwarded-Proto -- getScheme() / isSecure() 'SERVER_PORT' => 8888, 'HTTP_HOST' => 'localhost', 'REMOTE_ADDR' => '192.168.10.10',