Skip to content

Commit 45519d3

Browse files
authored
Fix #19437: Add support to specify request port by trusted proxies in \yii\web\Request::getServerPort()
1 parent f60730e commit 45519d3

File tree

4 files changed

+58
-3
lines changed

4 files changed

+58
-3
lines changed

docs/guide/runtime-requests.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,9 @@ You should not blindly trust headers provided by proxies unless you explicitly t
151151
Since 2.0.13 Yii supports configuring trusted proxies via the
152152
[[yii\web\Request::trustedHosts|trustedHosts]],
153153
[[yii\web\Request::secureHeaders|secureHeaders]],
154-
[[yii\web\Request::ipHeaders|ipHeaders]] and
155-
[[yii\web\Request::secureProtocolHeaders|secureProtocolHeaders]]
154+
[[yii\web\Request::ipHeaders|ipHeaders]],
155+
[[yii\web\Request::secureProtocolHeaders|secureProtocolHeaders]] and
156+
[[yii\web\Request::portHeaders|portHeaders]] (since 2.0.46)
156157
properties of the `request` component.
157158

158159
The following is a request config for an application that runs behind an array of reverse proxies,
@@ -184,6 +185,7 @@ In case your proxies are using different headers you can use the request configu
184185
'X-Forwarded-For',
185186
'X-Forwarded-Host',
186187
'X-Forwarded-Proto',
188+
'X-Forwarded-Port',
187189
'X-Proxy-User-Ip',
188190
'Front-End-Https',
189191
],

framework/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Yii Framework 2 Change Log
3535
- Enh #19416: Update and improve configurations for `yii\console\controllers\MessageController` (WinterSilence)
3636
- Bug #19403: Fix types in `yii\web\SessionIterator` (WinterSilence)
3737
- Enh #19420: Update list of JS callbacks in `yii\widgets\MaskedInput` (WinterSilence)
38+
- Enh #19437: Add support to specify request port by trusted proxies in `\yii\web\Request::getServerPort()` (rhertogh)
3839
- Bug #19445: Fix caching in `yii\i18n\Formatter::getUnitMessage()` (WinterSilence)
3940

4041

framework/web/Request.php

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ class Request extends \yii\base\Request
221221
'X-Forwarded-For',
222222
'X-Forwarded-Host',
223223
'X-Forwarded-Proto',
224+
'X-Forwarded-Port',
224225

225226
// Microsoft:
226227
'Front-End-Https',
@@ -241,6 +242,18 @@ class Request extends \yii\base\Request
241242
public $ipHeaders = [
242243
'X-Forwarded-For', // Common
243244
];
245+
/**
246+
* @var string[] List of headers where proxies store the real request port.
247+
* It's not advisable to put insecure headers here.
248+
* To use the `Forwarded Port`, the header must be added to [[secureHeaders]] list.
249+
* The match of header names is case-insensitive.
250+
* @see trustedHosts
251+
* @see secureHeaders
252+
* @since 2.0.46
253+
*/
254+
public $portHeaders = [
255+
'X-Forwarded-Port', // Common
256+
];
244257
/**
245258
* @var array list of headers to check for determining whether the connection is made via HTTPS.
246259
* The array keys are header names and the array value is a list of header values that indicate a secure connection.
@@ -1125,11 +1138,23 @@ public function getServerName()
11251138
}
11261139

11271140
/**
1128-
* Returns the server port number.
1141+
* Returns the server port number. If a port is specified via a forwarding header (e.g. 'X-Forwarded-Port')
1142+
* and the remote host is a "trusted host" the that port will be used (see [[portHeaders]]),
1143+
* otherwise the default server port will be returned.
11291144
* @return int|null server port number, null if not available
1145+
* @see portHeaders
11301146
*/
11311147
public function getServerPort()
11321148
{
1149+
foreach ($this->portHeaders as $portHeader) {
1150+
if ($this->headers->has($portHeader)) {
1151+
$port = $this->headers->get($portHeader);
1152+
if ($port !== null) {
1153+
return $port;
1154+
}
1155+
}
1156+
}
1157+
11331158
return isset($_SERVER['SERVER_PORT']) ? (int) $_SERVER['SERVER_PORT'] : null;
11341159
}
11351160

tests/framework/web/RequestTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,6 +1175,33 @@ public function testTrustedHostAndInjectedXForwardedFor($remoteAddress, $xForwar
11751175
$this->assertSame($expectedUserIp, $request->getUserIP());
11761176
}
11771177

1178+
public function trustedHostAndXForwardedPortDataProvider()
1179+
{
1180+
return [
1181+
'defaultPlain' => ['1.1.1.1', 80, null, null, 80],
1182+
'defaultSSL' => ['1.1.1.1', 443, null, null, 443],
1183+
'untrustedForwardedSSL' => ['1.1.1.1', 80, 443, ['10.0.0.0/8'], 80],
1184+
'untrustedForwardedPlain' => ['1.1.1.1', 443, 80, ['10.0.0.0/8'], 443],
1185+
'trustedForwardedSSL' => ['10.10.10.10', 80, 443, ['10.0.0.0/8'], 443],
1186+
'trustedForwardedPlain' => ['10.10.10.10', 443, 80, ['10.0.0.0/8'], 80],
1187+
];
1188+
}
1189+
1190+
/**
1191+
* @dataProvider trustedHostAndXForwardedPortDataProvider
1192+
*/
1193+
public function testTrustedHostAndXForwardedPort($remoteAddress, $requestPort, $xForwardedPort, $trustedHosts, $expectedPort)
1194+
{
1195+
$_SERVER['REMOTE_ADDR'] = $remoteAddress;
1196+
$_SERVER['SERVER_PORT'] = $requestPort;
1197+
$_SERVER['HTTP_X_FORWARDED_PORT'] = $xForwardedPort;
1198+
$params = [
1199+
'trustedHosts' => $trustedHosts,
1200+
];
1201+
$request = new Request($params);
1202+
$this->assertSame($expectedPort, $request->getServerPort());
1203+
}
1204+
11781205
/**
11791206
* @testWith ["POST", "GET", "POST"]
11801207
* ["POST", "OPTIONS", "POST"]

0 commit comments

Comments
 (0)