diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index 99970efe27..34249c624a 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -20,7 +20,9 @@ namespace Symfony\Component\HttpFoundation; */ class Request { - protected static $trustProxy = false; + protected static $trustProxyData = false; + + protected static $trustedProxies = array(); /** * @var \Symfony\Component\HttpFoundation\ParameterBag @@ -357,14 +359,26 @@ class Request /** * Trusts $_SERVER entries coming from proxies. * - * You should only call this method if your application - * is hosted behind a reverse proxy that you manage. - * - * @api + * @deprecated Deprecated since version 2.0, to be removed in 2.3. Use setTrustedProxies instead. */ public static function trustProxyData() { - self::$trustProxy = true; + self::$trustProxyData = true; + } + + /** + * Sets a list of trusted proxies. + * + * You should only list the reverse proxies that you manage directly. + * + * @param array $proxies A list of trusted proxies + * + * @api + */ + public static function setTrustedProxies(array $proxies) + { + self::$trustedProxies = $proxies; + self::$trustProxyData = $proxies ? true : false; } /** @@ -441,23 +455,41 @@ class Request /** * Returns the client IP address. * - * @param Boolean $proxy Whether the current request has been made behind a proxy or not + * This method can read the client IP address from the "X-Forwarded-For" header + * when trusted proxies were set via "setTrustedProxies()". The "X-Forwarded-For" + * header value is a comma+space separated list of IP addresses, the left-most + * being the original client, and each successive proxy that passed the request + * adding the IP address where it received the request from. + * + * @param Boolean $proxy Whether the current request has been made behind a proxy or not (deprecated) * * @return string The client IP address * + * @see http://en.wikipedia.org/wiki/X-Forwarded-For + * + * @deprecated The proxy argument is deprecated since version 2.0 and will be removed in 2.3. Use setTrustedProxies instead. + * * @api */ public function getClientIp($proxy = false) { - if ($proxy) { - if (self::$trustProxy && $this->server->has('HTTP_X_FORWARDED_FOR')) { - $clientIp = explode(',', $this->server->get('HTTP_X_FORWARDED_FOR'), 2); + $ip = $this->server->get('REMOTE_ADDR'); - return isset($clientIp[0]) ? trim($clientIp[0]) : ''; - } + if (!$proxy && !self::$trustProxyData) { + return $ip; } - return $this->server->get('REMOTE_ADDR'); + if (!$this->server->has('HTTP_X_FORWARDED_FOR')) { + return $ip; + } + + $clientIps = array_map('trim', explode(',', $this->server->get('HTTP_X_FORWARDED_FOR'))); + $clientIps[] = $ip; + + $trustedProxies = ($proxy || self::$trustProxyData) && !self::$trustedProxies ? array($ip) : self::$trustedProxies; + $clientIps = array_diff($clientIps, $trustedProxies); + + return array_pop($clientIps); } /** @@ -560,7 +592,7 @@ class Request */ public function getPort() { - if (self::$trustProxy && $this->headers->has('X-Forwarded-Port')) { + if (self::$trustProxyData && $this->headers->has('X-Forwarded-Port')) { return $this->headers->get('X-Forwarded-Port'); } @@ -683,9 +715,9 @@ class Request return ( (strtolower($this->server->get('HTTPS')) == 'on' || $this->server->get('HTTPS') == 1) || - (self::$trustProxy && strtolower($this->headers->get('SSL_HTTPS')) == 'on' || $this->headers->get('SSL_HTTPS') == 1) + (self::$trustProxyData && strtolower($this->headers->get('SSL_HTTPS')) == 'on' || $this->headers->get('SSL_HTTPS') == 1) || - (self::$trustProxy && strtolower($this->headers->get('X_FORWARDED_PROTO')) == 'https') + (self::$trustProxyData && strtolower($this->headers->get('X_FORWARDED_PROTO')) == 'https') ); } @@ -698,7 +730,7 @@ class Request */ public function getHost() { - if (self::$trustProxy && $host = $this->headers->get('X_FORWARDED_HOST')) { + if (self::$trustProxyData && $host = $this->headers->get('X_FORWARDED_HOST')) { $elements = explode(',', $host); $host = trim($elements[count($elements) - 1]); diff --git a/tests/Symfony/Tests/Component/HttpFoundation/RequestTest.php b/tests/Symfony/Tests/Component/HttpFoundation/RequestTest.php index 3042c5cfcd..c9b164581e 100644 --- a/tests/Symfony/Tests/Component/HttpFoundation/RequestTest.php +++ b/tests/Symfony/Tests/Component/HttpFoundation/RequestTest.php @@ -19,11 +19,6 @@ use Symfony\Component\HttpFoundation\Request; class RequestTest extends \PHPUnit_Framework_TestCase { - public function setUp() - { - Request::trustProxyData(); - } - /** * @covers Symfony\Component\HttpFoundation\Request::__construct */ @@ -430,15 +425,17 @@ class RequestTest extends \PHPUnit_Framework_TestCase $request->initialize(array(), array(), array(), array(), array(), array('HTTP_HOST' => 'www.exemple.com')); $this->assertEquals('www.exemple.com', $request->getHost(), '->getHost() from Host Header'); - // Host header with port number. + // Host header with port number $request->initialize(array(), array(), array(), array(), array(), array('HTTP_HOST' => 'www.exemple.com:8080')); $this->assertEquals('www.exemple.com', $request->getHost(), '->getHost() from Host Header with port number'); - // Server values. + // Server values $request->initialize(array(), array(), array(), array(), array(), array('SERVER_NAME' => 'www.exemple.com')); $this->assertEquals('www.exemple.com', $request->getHost(), '->getHost() from server name'); - // X_FORWARDED_HOST. + Request::setTrustedProxies(array('1.1.1.1')); + + // X_FORWARDED_HOST $request->initialize(array(), array(), array(), array(), array(), array('HTTP_X_FORWARDED_HOST' => 'www.exemple.com')); $this->assertEquals('www.exemple.com', $request->getHost(), '->getHost() from X_FORWARDED_HOST'); @@ -458,6 +455,8 @@ class RequestTest extends \PHPUnit_Framework_TestCase $request->initialize(array(), array(), array(), array(), array(), array('SERVER_NAME' => 'www.exemple.com', 'HTTP_HOST' => 'www.host.com')); $this->assertEquals('www.host.com', $request->getHost(), '->getHost() value from Host header has priority over SERVER_NAME '); + + Request::setTrustedProxies(array()); } /** @@ -496,17 +495,19 @@ class RequestTest extends \PHPUnit_Framework_TestCase /** * @dataProvider testGetClientIpProvider */ - public function testGetClientIp($expected, $proxy, $remoteAddr, $httpForwardedFor) + public function testGetClientIp($expected, $proxy, $remoteAddr, $httpForwardedFor, $trustedProxies) { - $request = new Request; - $this->assertEquals('', $request->getClientIp()); - $this->assertEquals('', $request->getClientIp(true)); + $request = new Request(); $server = array('REMOTE_ADDR' => $remoteAddr); if (null !== $httpForwardedFor) { $server['HTTP_X_FORWARDED_FOR'] = $httpForwardedFor; } + if ($proxy || $trustedProxies) { + Request::setTrustedProxies(null === $trustedProxies ? array($remoteAddr) : $trustedProxies); + } + $request->initialize(array(), array(), array(), array(), array(), $server); $this->assertEquals($expected, $request->getClientIp($proxy)); } @@ -514,13 +515,15 @@ class RequestTest extends \PHPUnit_Framework_TestCase public function testGetClientIpProvider() { return array( - array('88.88.88.88', false, '88.88.88.88', null), - array('127.0.0.1', false, '127.0.0.1', null), - array('127.0.0.1', false, '127.0.0.1', '88.88.88.88'), - array('88.88.88.88', true, '127.0.0.1', '88.88.88.88'), - array('::1', false, '::1', null), - array('2620:0:1cfe:face:b00c::3', true, '::1', '2620:0:1cfe:face:b00c::3, ::1'), - array('88.88.88.88', true, '123.45.67.89', '88.88.88.88, 87.65.43.21, 127.0.0.1'), + array('88.88.88.88', false, '88.88.88.88', null, null), + array('127.0.0.1', false, '127.0.0.1', null, null), + array('::1', false, '::1', null, null), + array('127.0.0.1', false, '127.0.0.1', '88.88.88.88', null), + array('88.88.88.88', true, '127.0.0.1', '88.88.88.88', null), + array('2620:0:1cfe:face:b00c::3', true, '::1', '2620:0:1cfe:face:b00c::3', null), + array('88.88.88.88', true, '123.45.67.89', '127.0.0.1, 87.65.43.21, 88.88.88.88', null), + array('87.65.43.21', true, '123.45.67.89', '127.0.0.1, 87.65.43.21, 88.88.88.88', array('123.45.67.89', '88.88.88.88')), + array('87.65.43.21', false, '123.45.67.89', '127.0.0.1, 87.65.43.21, 88.88.88.88', array('123.45.67.89', '88.88.88.88')), ); }