Skip to content

Commit

Permalink
updated TrustedProxy for Symfony 4 and Laravel 5.6
Browse files Browse the repository at this point in the history
  • Loading branch information
fideloper committed Dec 19, 2017
1 parent bd53593 commit 1d09591
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 226 deletions.
6 changes: 3 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
"illuminate/contracts": "~5.0"
},
"require-dev": {
"illuminate/http": "~5.0",
"mockery/mockery": "~0.9.3",
"phpunit/phpunit": "^5.7"
"illuminate/http": "~5.6",
"mockery/mockery": "~1.0",
"phpunit/phpunit": "^6.0"
},
"autoload": {
"psr-4": {
Expand Down
53 changes: 18 additions & 35 deletions config/trustedproxy.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,48 +24,31 @@
* how many proxies that client's request has
* subsequently passed through.
*/
'proxies' => [
'192.168.1.10',
],
'proxies' => null, // [<ip addresses>,], '*'

/*
* Or, to trust all proxies that connect
* directly to your server, uncomment this:
* To trust one or more specific proxies that connect
* directly to your server, use an array of IP addresses:
*/
# 'proxies' => '*',
# 'proxies' => ['192.168.1.1'],

/*
* Or, to trust ALL proxies, including those that
* are in a chain of forwarding, uncomment this:
*/
# 'proxies' => '**',
* Or, to trust all proxies that connect
* directly to your server, use a "*"
*/
# 'proxies' => '*',

/*
* Default Header Names
*
* Change these if the proxy does
* not send the default header names.
*
* Note that headers such as X-Forwarded-For
* are transformed to HTTP_X_FORWARDED_FOR format.
*
* The following are Symfony defaults, found in
* \Symfony\Component\HttpFoundation\Request::$trustedHeaders
*
* You may optionally set headers to 'null' here if you'd like
* for them to be considered untrusted instead. Ex:
*
* Illuminate\Http\Request::HEADER_CLIENT_HOST => null,
* Which headers to use to detect proxy related data (For, Host, Proto, Port)
*
* WARNING: If you're using AWS Elastic Load Balancing or Heroku,
* the FORWARDED and X_FORWARDED_HOST headers should be set to null
* as they are currently unsupported there.
* Options include:
*
* - Illuminate\Http\Request::HEADER_X_FORWARDED_ALL (use all x-forwarded-* headers to establish trust)
* - Illuminate\Http\Request::HEADER_FORWARDED (use the FORWARDED header to establish trust)
*
* @link https://symfony.com/doc/current/deployment/proxies.html
*/
'headers' => [
(defined('Illuminate\Http\Request::HEADER_FORWARDED') ? Illuminate\Http\Request::HEADER_FORWARDED : 'forwarded') => 'FORWARDED',
Illuminate\Http\Request::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
Illuminate\Http\Request::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST',
Illuminate\Http\Request::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
Illuminate\Http\Request::HEADER_CLIENT_PORT => 'X_FORWARDED_PORT',
]
'headers' => Illuminate\Http\Request::HEADER_X_FORWARDED_ALL,


];
83 changes: 9 additions & 74 deletions src/TrustProxies.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Fideloper\Proxy;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Contracts\Config\Repository;

class TrustProxies
Expand Down Expand Up @@ -48,9 +49,9 @@ public function __construct(Repository $config)
*
* @return mixed
*/
public function handle($request, Closure $next)
public function handle(Request $request, Closure $next)
{
$this->setTrustedProxyHeaderNames($request);
$request::setTrustedProxies([], $this->getTrustedHeaderNames()); // Reset trusted proxies between requests
$this->setTrustedProxyIpAddresses($request);

return $next($request);
Expand All @@ -61,7 +62,7 @@ public function handle($request, Closure $next)
*
* @param \Illuminate\Http\Request $request
*/
protected function setTrustedProxyIpAddresses($request)
protected function setTrustedProxyIpAddresses(Request $request)
{
$trustedIps = $this->proxies ?: $this->config->get('trustedproxy.proxies');

Expand All @@ -78,13 +79,6 @@ protected function setTrustedProxyIpAddresses($request)
if ($trustedIps === '*') {
return $this->setTrustedProxyIpAddressesToTheCallingIp($request);
}

// We trust all proxies. Those that call us, and those that are
// further up the calling chain (e.g., where the X-FORWARDED-FOR
// header has multiple IP addresses listed);
if ($trustedIps === '**') {
return $this->setTrustedProxyIpAddressesToAllIps($request);
}
}

/**
Expand All @@ -93,87 +87,28 @@ protected function setTrustedProxyIpAddresses($request)
* @param \Illuminate\Http\Request $request
* @param array $trustedIps
*/
private function setTrustedProxyIpAddressesToSpecificIps($request, $trustedIps)
private function setTrustedProxyIpAddressesToSpecificIps(Request $request, $trustedIps)
{
$request->setTrustedProxies((array) $trustedIps, $this->getTrustedHeaderSet());
$request->setTrustedProxies((array) $trustedIps, $this->getTrustedHeaderNames());
}

/**
* We set the trusted proxy to be the first IP addresses received.
*
* @param \Illuminate\Http\Request $request
*/
private function setTrustedProxyIpAddressesToTheCallingIp($request)
private function setTrustedProxyIpAddressesToTheCallingIp(Request $request)
{
$request->setTrustedProxies($request->getClientIps(), $this->getTrustedHeaderSet());
$request->setTrustedProxies([$request->server->get('REMOTE_ADDR')], $this->getTrustedHeaderNames());
}

/**
* Trust all IP Addresses.
*
* @param \Illuminate\Http\Request $request
*/
private function setTrustedProxyIpAddressesToAllIps($request)
{
// 0.0.0.0/0 is the CIDR for all ipv4 addresses
// 2000:0:0:0:0:0:0:0/3 is the CIDR for all ipv6 addresses currently
// allocated http://www.iana.org/assignments/ipv6-unicast-address-assignments/ipv6-unicast-address-assignments.xhtml
$request->setTrustedProxies(['0.0.0.0/0', '2000:0:0:0:0:0:0:0/3'], $this->getTrustedHeaderSet());
}

/**
* Set the trusted header names based on the content of trustedproxy.headers.
*
* @deprecated Since Symfony 3.3+, but available for backwards compatibility.
*
* @param \Illuminate\Http\Request $request
*/
protected function setTrustedProxyHeaderNames($request)
{
$trustedHeaderNames = $this->getTrustedHeaderNames();

if(!is_array($trustedHeaderNames)) { return; } // Leave the defaults

foreach ($trustedHeaderNames as $headerKey => $headerName) {
$request->setTrustedHeaderName($headerKey, $headerName);
}
}

/**
* Retrieve trusted header names, falling back to defaults if config not set.
* Retrieve trusted header name(s), falling back to defaults if config not set.
*
* @return array
*/
protected function getTrustedHeaderNames()
{
return $this->headers ?: $this->config->get('trustedproxy.headers');
}

/**
* Construct bit field integer of the header set that setTrustedProxies() expects.
*
* @return int
*/
protected function getTrustedHeaderSet()
{
$trustedHeaderNames = $this->getTrustedHeaderNames();
$headerKeys = array_keys($this->getTrustedHeaderNames());

return array_reduce($headerKeys, function ($set, $key) use ($trustedHeaderNames) {
// PHP 7+ gives a warning if non-numeric value is used
// resulting in a thrown ErrorException within Laravel
// This error occurs with Symfony < 3.3, PHP7+
if(! is_numeric($key)) {
return $set;
}

// If the header value is null, it is a distrusted header,
// so we will ignore it and move on.
if (is_null($trustedHeaderNames[$key])) {
return $set;
}

return $set | $key;
}, 0);
}
}
Loading

0 comments on commit 1d09591

Please sign in to comment.