Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion src/Illuminate/Http/Middleware/TrustProxies.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ class TrustProxies
*/
protected $proxies;

/**
* The number of proxy layers to trust.
*
* @var int
*/
protected $layers = 1;

/**
* The proxy header mappings.
*
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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;
}
}
52 changes: 49 additions & 3 deletions tests/Http/Middleware/TrustProxiesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
}
};
}
Expand Down