Added new Forwarded header support for Request::getClientIps
This commit is contained in:
parent
51a8b11ecd
commit
4c8a25a6e2
|
@ -30,6 +30,7 @@ use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
|||
*/
|
||||
class Request
|
||||
{
|
||||
const HEADER_FORWARDED = 'forwarded';
|
||||
const HEADER_CLIENT_IP = 'client_ip';
|
||||
const HEADER_CLIENT_HOST = 'client_host';
|
||||
const HEADER_CLIENT_PROTO = 'client_proto';
|
||||
|
@ -46,6 +47,9 @@ class Request
|
|||
const METHOD_TRACE = 'TRACE';
|
||||
const METHOD_CONNECT = 'CONNECT';
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected static $trustedProxies = array();
|
||||
|
||||
/**
|
||||
|
@ -62,10 +66,13 @@ class Request
|
|||
* Names for headers that can be trusted when
|
||||
* using trusted proxies.
|
||||
*
|
||||
* The default names are non-standard, but widely used
|
||||
* The FORWARDED header is the standard as of rfc7239.
|
||||
*
|
||||
* The other headers are non-standard, but widely used
|
||||
* by popular reverse proxies (like Apache mod_proxy or Amazon EC2).
|
||||
*/
|
||||
protected static $trustedHeaders = array(
|
||||
self::HEADER_FORWARDED => 'FORWARDED',
|
||||
self::HEADER_CLIENT_IP => 'X_FORWARDED_FOR',
|
||||
self::HEADER_CLIENT_HOST => 'X_FORWARDED_HOST',
|
||||
self::HEADER_CLIENT_PROTO => 'X_FORWARDED_PROTO',
|
||||
|
@ -823,24 +830,26 @@ class Request
|
|||
*/
|
||||
public function getClientIps()
|
||||
{
|
||||
$clientIps = array();
|
||||
$ip = $this->server->get('REMOTE_ADDR');
|
||||
|
||||
if (!self::$trustedProxies) {
|
||||
return array($ip);
|
||||
}
|
||||
|
||||
if (!self::$trustedHeaders[self::HEADER_CLIENT_IP] || !$this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP])) {
|
||||
return array($ip);
|
||||
if (self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) {
|
||||
$forwardedHeader = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]);
|
||||
preg_match_all('{(for)=("?\[?)([a-z0-9\.:_\-/]*)}', $forwardedHeader, $matches);
|
||||
$clientIps = $matches[3];
|
||||
} elseif (self::$trustedHeaders[self::HEADER_CLIENT_IP] && $this->headers->has(self::$trustedHeaders[self::HEADER_CLIENT_IP])) {
|
||||
$clientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP])));
|
||||
}
|
||||
|
||||
$clientIps = array_map('trim', explode(',', $this->headers->get(self::$trustedHeaders[self::HEADER_CLIENT_IP])));
|
||||
$clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from
|
||||
|
||||
$ip = $clientIps[0]; // Fallback to this when the client IP falls into the range of trusted proxies
|
||||
|
||||
// Eliminate all IPs from the forwarded IP chain which are trusted proxies
|
||||
foreach ($clientIps as $key => $clientIp) {
|
||||
// Remove port on IPv4 address (unfortunately, it does happen)
|
||||
// Remove port (unfortunately, it does happen)
|
||||
if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) {
|
||||
$clientIps[$key] = $clientIp = $match[1];
|
||||
}
|
||||
|
|
|
@ -875,6 +875,31 @@ class RequestTest extends \PHPUnit_Framework_TestCase
|
|||
Request::setTrustedProxies(array());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider testGetClientIpsForwardedProvider
|
||||
*/
|
||||
public function testGetClientIpsForwarded($expected, $remoteAddr, $httpForwarded, $trustedProxies)
|
||||
{
|
||||
$request = $this->getRequestInstanceForClientIpsForwardedTests($remoteAddr, $httpForwarded, $trustedProxies);
|
||||
|
||||
$this->assertEquals($expected, $request->getClientIps());
|
||||
|
||||
Request::setTrustedProxies(array());
|
||||
}
|
||||
|
||||
public function testGetClientIpsForwardedProvider()
|
||||
{
|
||||
// $expected $remoteAddr $httpForwarded $trustedProxies
|
||||
return array(
|
||||
array(array('127.0.0.1'), '127.0.0.1', 'for="_gazonk"', null),
|
||||
array(array('_gazonk'), '127.0.0.1', 'for="_gazonk"', array('127.0.0.1')),
|
||||
array(array('88.88.88.88'), '127.0.0.1', 'for="88.88.88.88:80"', array('127.0.0.1')),
|
||||
array(array('192.0.2.60'), '::1', 'for=192.0.2.60;proto=http;by=203.0.113.43', array('::1')),
|
||||
array(array('2620:0:1cfe:face:b00c::3', '192.0.2.43'), '::1', 'for=192.0.2.43, for=2620:0:1cfe:face:b00c::3', array('::1')),
|
||||
array(array('2001:db8:cafe::17'), '::1', 'for="[2001:db8:cafe::17]:4711', array('::1')),
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetClientIpsProvider()
|
||||
{
|
||||
// $expected $remoteAddr $httpForwardedFor $trustedProxies
|
||||
|
@ -1467,6 +1492,25 @@ class RequestTest extends \PHPUnit_Framework_TestCase
|
|||
return $request;
|
||||
}
|
||||
|
||||
private function getRequestInstanceForClientIpsForwardedTests($remoteAddr, $httpForwarded, $trustedProxies)
|
||||
{
|
||||
$request = new Request();
|
||||
|
||||
$server = array('REMOTE_ADDR' => $remoteAddr);
|
||||
|
||||
if (null !== $httpForwarded) {
|
||||
$server['HTTP_FORWARDED'] = $httpForwarded;
|
||||
}
|
||||
|
||||
if ($trustedProxies) {
|
||||
Request::setTrustedProxies($trustedProxies);
|
||||
}
|
||||
|
||||
$request->initialize(array(), array(), array(), array(), array(), $server);
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
||||
public function testTrustedProxies()
|
||||
{
|
||||
$request = Request::create('http://example.com/');
|
||||
|
|
Reference in New Issue