diff --git a/src/Illuminate/Http/Middleware/TrustProxies.php b/src/Illuminate/Http/Middleware/TrustProxies.php index 81906c1f1951..86d0d45b5455 100644 --- a/src/Illuminate/Http/Middleware/TrustProxies.php +++ b/src/Illuminate/Http/Middleware/TrustProxies.php @@ -14,6 +14,13 @@ class TrustProxies */ protected $proxies; + /** + * The number of proxy layers to trust. + * + * @var int + */ + protected $layers = 1; + /** * The proxy header mappings. * @@ -50,7 +57,7 @@ protected function setTrustedProxyIpAddresses(Request $request) $trustedIps = $this->proxies() ?: config('trustedproxy.proxies'); if ($trustedIps === '*' || $trustedIps === '**') { - return $this->setTrustedProxyIpAddressesToTheCallingIp($request); + return $this->setTrustedProxyIpAddressesToLayers($request, $this->layers()); } $trustedIps = is_string($trustedIps) @@ -85,6 +92,27 @@ protected function setTrustedProxyIpAddressesToTheCallingIp(Request $request) $request->setTrustedProxies([$request->server->get('REMOTE_ADDR')], $this->getTrustedHeaderNames()); } + /** + * Trust the most recent $layers number of proxies. + * + * @param Request $request + * @param int $layers + * @return void + */ + protected function setTrustedProxyIpAddressesToLayers(Request $request, int $layers): void + { + $trustedIps = array_slice( + array_reverse(array_filter(explode(', ', $request->headers->get('X_FORWARDED_FOR')))), + 0, + $layers - 1, + ); + + $request->setTrustedProxies( + array_merge([$request->server->get('REMOTE_ADDR')], $trustedIps), + $this->getTrustedHeaderNames() + ); + } + /** * Retrieve trusted header name(s), falling back to defaults if config not set. * @@ -117,4 +145,14 @@ protected function proxies() { return $this->proxies; } + + /** + * Get the number of proxy layers to trust. + * + * @return int + */ + protected function layers() + { + return $this->layers; + } } diff --git a/tests/Http/Middleware/TrustProxiesTest.php b/tests/Http/Middleware/TrustProxiesTest.php index 7ad01d1b98cf..380a36d0b316 100644 --- a/tests/Http/Middleware/TrustProxiesTest.php +++ b/tests/Http/Middleware/TrustProxiesTest.php @@ -79,6 +79,50 @@ public function test_trusted_proxy_sets_trusted_proxies_with_double_wildcard_for }); } + /** + * Test that TrustedProxies will apply a wildcard up to two layers of proxies deep. + */ + public function test_trusted_proxy_sets_trusted_proxies_for_2_layers() + { + $trustedProxy = $this->createTrustedProxy($this->headerAll, '*', 2); + $forwardedFor = [ + '' => '192.168.10.10', + '192.0.2.2' => '192.0.2.2', + '192.0.2.2, 192.0.2.199' => '192.0.2.2', + '192.0.2.2, 192.0.2.199, 99.99.99.99' => '192.0.2.199', + ]; + + foreach ($forwardedFor as $forwardedForHeader => $expected) { + $request = $this->createProxiedRequest(['HTTP_X_FORWARDED_FOR' => $forwardedForHeader]); + + $trustedProxy->handle($request, function ($request) use ($expected) { + $this->assertSame($expected, $request->getClientIp(), 'Assert trusted proxy x-forwarded-for header used 2 layers deep'); + }); + } + } + + /** + * Test that TrustedProxies will apply a wildcard up to n layers of proxies deep. + */ + public function test_trusted_proxy_sets_trusted_proxies_for_n_layers() + { + $forwardedFor = '192.0.2.2, 192.0.2.199, 99.99.99.99'; + $layers = [ + 1 => '99.99.99.99', + 2 => '192.0.2.199', + 3 => '192.0.2.2', + ]; + + foreach ($layers as $layer => $expected) { + $trustedProxy = $this->createTrustedProxy($this->headerAll, '*', $layer); + $request = $this->createProxiedRequest(['HTTP_X_FORWARDED_FOR' => $forwardedFor]); + + $trustedProxy->handle($request, function ($request) use ($expected) { + $this->assertSame($expected, $request->getClientIp(), 'Assert trusted proxy x-forwarded-for header used n layers deep'); + }); + } + } + /** * Test the most typical usage of TrustProxies: * Trusted X-Forwarded-For header. @@ -392,16 +436,18 @@ protected function createProxiedRequest($serverOverrides = []) * * @param null|string|int $trustedHeaders * @param null|array|string $trustedProxies + * @param int $layers * @return \Illuminate\Http\Middleware\TrustProxies */ - protected function createTrustedProxy($trustedHeaders, $trustedProxies) + protected function createTrustedProxy($trustedHeaders, $trustedProxies, $layers = 1) { - return new class($trustedHeaders, $trustedProxies) extends TrustProxies + return new class($trustedHeaders, $trustedProxies, $layers) extends TrustProxies { - public function __construct($trustedHeaders, $trustedProxies) + public function __construct($trustedHeaders, $trustedProxies, $layers) { $this->headers = $trustedHeaders; $this->proxies = $trustedProxies; + $this->layers = $layers; } }; }