From 7bafb7bf09e181c4317ea5a59169bf3cc948d8dc Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Wed, 7 Feb 2018 00:01:50 +0300 Subject: [PATCH] Fixes #14488: Added support for X-Forwarded-Host to `yii\web\Request`, fixed `getServerPort()` usage --- framework/CHANGELOG.md | 1 + framework/web/Request.php | 20 +++++--- tests/framework/web/RequestTest.php | 80 ++++++++++++++++++++++++++++- 3 files changed, 94 insertions(+), 7 deletions(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 245fb8f42c..54b6c53df6 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -4,6 +4,7 @@ Yii Framework 2 Change Log 2.0.14 under development ------------------------ +- Enh #14488: Added support for X-Forwarded-Host to `yii\web\Request`, fixed `getServerPort()` usage (si294r, samdark) - Enh #13019: Support JSON in SchemaBuilderTrait (zhukovra, undefinedor) - Enh #15047: `yii\db\Query::select()` and `yii\db\Query::addSelect()` now check for duplicate column names (wapmorgan) - Enh #14643: Added `yii\web\ErrorAction::$layout` property to conveniently set layout from error action config (swods, cebe, samdark) diff --git a/framework/web/Request.php b/framework/web/Request.php index 8a63790c98..0cbf720abe 100644 --- a/framework/web/Request.php +++ b/framework/web/Request.php @@ -218,9 +218,12 @@ class Request extends \yii\base\Request * @since 2.0.13 */ public $secureHeaders = [ + // Common: 'X-Forwarded-For', 'X-Forwarded-Host', 'X-Forwarded-Proto', + + // Microsoft: 'Front-End-Https', 'X-Rewrite-Url', ]; @@ -233,7 +236,7 @@ class Request extends \yii\base\Request * @since 2.0.13 */ public $ipHeaders = [ - 'X-Forwarded-For', + 'X-Forwarded-For', // Common ]; /** * @var array list of headers to check for determining whether the connection is made via HTTPS. @@ -245,8 +248,8 @@ class Request extends \yii\base\Request * @since 2.0.13 */ public $secureProtocolHeaders = [ - 'X-Forwarded-Proto' => ['https'], - 'Front-End-Https' => ['on'], + 'X-Forwarded-Proto' => ['https'], // Common + 'Front-End-Https' => ['on'], // Microsoft ]; /** @@ -705,7 +708,10 @@ class Request extends \yii\base\Request if ($this->_hostInfo === null) { $secure = $this->getIsSecureConnection(); $http = $secure ? 'https' : 'http'; - if ($this->headers->has('Host')) { + + if ($this->headers->has('X-Forwarded-Host')) { + $this->_hostInfo = $http . '://' . $this->headers->get('X-Forwarded-Host'); + } elseif ($this->headers->has('Host')) { $this->_hostInfo = $http . '://' . $this->headers->get('Host'); } elseif (isset($_SERVER['SERVER_NAME'])) { $this->_hostInfo = $http . '://' . $_SERVER['SERVER_NAME']; @@ -1217,7 +1223,8 @@ class Request extends \yii\base\Request public function getPort() { if ($this->_port === null) { - $this->_port = !$this->getIsSecureConnection() && isset($_SERVER['SERVER_PORT']) ? (int) $_SERVER['SERVER_PORT'] : 80; + $serverPort = $this->getServerPort(); + $this->_port = !$this->getIsSecureConnection() && $serverPort !== null ? $serverPort : 80; } return $this->_port; @@ -1249,7 +1256,8 @@ class Request extends \yii\base\Request public function getSecurePort() { if ($this->_securePort === null) { - $this->_securePort = $this->getIsSecureConnection() && isset($_SERVER['SERVER_PORT']) ? (int) $_SERVER['SERVER_PORT'] : 443; + $serverPort = $this->getServerPort(); + $this->_securePort = $this->getIsSecureConnection() && $serverPort !== null ? $serverPort : 443; } return $this->_securePort; diff --git a/tests/framework/web/RequestTest.php b/tests/framework/web/RequestTest.php index 589ab2d069..08f12b0c33 100644 --- a/tests/framework/web/RequestTest.php +++ b/tests/framework/web/RequestTest.php @@ -266,7 +266,85 @@ class RequestTest extends TestCase $this->assertEquals($_GET, ['id' => 63]); } - public function testGetHostInfo() + public function getHostInfoDataProvider() + { + return [ + // empty + [ + [], + [null, null] + ], + // normal + [ + [ + 'HTTP_HOST' => 'example1.com', + 'SERVER_NAME' => 'example2.com', + ], + [ + 'http://example1.com', + 'example1.com', + ] + ], + // HTTP header missing + [ + [ + 'SERVER_NAME' => 'example2.com', + ], + [ + 'http://example2.com', + 'example2.com', + ] + ], + // forwarded from untrusted server + [ + [ + 'HTTP_X_FORWARDED_HOST' => 'example3.com', + 'HTTP_HOST' => 'example1.com', + 'SERVER_NAME' => 'example2.com', + ], + [ + 'http://example1.com', + 'example1.com', + ] + ], + // forwarded from trusted proxy + [ + [ + 'HTTP_X_FORWARDED_HOST' => 'example3.com', + 'HTTP_HOST' => 'example1.com', + 'SERVER_NAME' => 'example2.com', + 'REMOTE_ADDR' => '192.168.0.1', + ], + [ + 'http://example3.com', + 'example3.com', + ] + ], + ]; + } + + /** + * @dataProvider getHostInfoDataProvider + * @param array $server + * @param array $expected + */ + public function testGetHostInfo($server, $expected) + { + $original = $_SERVER; + $_SERVER = $server; + $request = new Request([ + 'trustedHosts' => [ + '192.168.0.0/24', + ], + ]); + + $this->assertEquals($expected[0], $request->getHostInfo()); + $this->assertEquals($expected[1], $request->getHostName()); + $_SERVER = $original; + } + + + public function testSetHostInfo() { $request = new Request();