Merge pull request #13116 from nextcloud/fix/only_trust_xforwardedhost_for_trusted_proxies

Only trust the X-FORWARDED-HOST header for trusted proxies
This commit is contained in:
Roeland Jago Douma 2018-12-19 09:47:44 +01:00 committed by GitHub
commit a0ce0824bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 98 additions and 55 deletions

View File

@ -691,7 +691,7 @@ class Request implements \ArrayAccess, \Countable, IRequest {
return $this->config->getSystemValue('overwriteprotocol'); return $this->config->getSystemValue('overwriteprotocol');
} }
if (isset($this->server['HTTP_X_FORWARDED_PROTO'])) { if ($this->fromTrustedProxy() && isset($this->server['HTTP_X_FORWARDED_PROTO'])) {
if (strpos($this->server['HTTP_X_FORWARDED_PROTO'], ',') !== false) { if (strpos($this->server['HTTP_X_FORWARDED_PROTO'], ',') !== false) {
$parts = explode(',', $this->server['HTTP_X_FORWARDED_PROTO']); $parts = explode(',', $this->server['HTTP_X_FORWARDED_PROTO']);
$proto = strtolower(trim($parts[0])); $proto = strtolower(trim($parts[0]));
@ -862,7 +862,7 @@ class Request implements \ArrayAccess, \Countable, IRequest {
*/ */
public function getInsecureServerHost(): string { public function getInsecureServerHost(): string {
$host = 'localhost'; $host = 'localhost';
if (isset($this->server['HTTP_X_FORWARDED_HOST'])) { if ($this->fromTrustedProxy() && isset($this->server['HTTP_X_FORWARDED_HOST'])) {
if (strpos($this->server['HTTP_X_FORWARDED_HOST'], ',') !== false) { if (strpos($this->server['HTTP_X_FORWARDED_HOST'], ',') !== false) {
$parts = explode(',', $this->server['HTTP_X_FORWARDED_HOST']); $parts = explode(',', $this->server['HTTP_X_FORWARDED_HOST']);
$host = trim(current($parts)); $host = trim(current($parts));
@ -924,4 +924,10 @@ class Request implements \ArrayAccess, \Countable, IRequest {
return null; return null;
} }
private function fromTrustedProxy(): bool {
$remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
$trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
return \is_array($trustedProxies) && $this->isTrustedProxy($trustedProxies, $remoteAddress);
}
} }

View File

@ -715,15 +715,20 @@ class RequestTest extends \Test\TestCase {
public function testGetServerProtocolWithProtoValid() { public function testGetServerProtocolWithProtoValid() {
$this->config $this->config
->expects($this->exactly(2))
->method('getSystemValue') ->method('getSystemValue')
->with('overwriteprotocol') ->will($this->returnCallback(function($key, $default) {
->will($this->returnValue('')); if ($key === 'trusted_proxies') {
return ['1.2.3.4'];
}
return $default;
}));
$requestHttps = new Request( $requestHttps = new Request(
[ [
'server' => [ 'server' => [
'HTTP_X_FORWARDED_PROTO' => 'HtTpS' 'HTTP_X_FORWARDED_PROTO' => 'HtTpS',
'REMOTE_ADDR' => '1.2.3.4',
], ],
], ],
$this->secureRandom, $this->secureRandom,
@ -734,7 +739,8 @@ class RequestTest extends \Test\TestCase {
$requestHttp = new Request( $requestHttp = new Request(
[ [
'server' => [ 'server' => [
'HTTP_X_FORWARDED_PROTO' => 'HTTp' 'HTTP_X_FORWARDED_PROTO' => 'HTTp',
'REMOTE_ADDR' => '1.2.3.4',
], ],
], ],
$this->secureRandom, $this->secureRandom,
@ -750,10 +756,10 @@ class RequestTest extends \Test\TestCase {
public function testGetServerProtocolWithHttpsServerValueOn() { public function testGetServerProtocolWithHttpsServerValueOn() {
$this->config $this->config
->expects($this->once())
->method('getSystemValue') ->method('getSystemValue')
->with('overwriteprotocol') ->will($this->returnCallback(function($key, $default) {
->will($this->returnValue('')); return $default;
}));
$request = new Request( $request = new Request(
[ [
@ -771,10 +777,10 @@ class RequestTest extends \Test\TestCase {
public function testGetServerProtocolWithHttpsServerValueOff() { public function testGetServerProtocolWithHttpsServerValueOff() {
$this->config $this->config
->expects($this->once())
->method('getSystemValue') ->method('getSystemValue')
->with('overwriteprotocol') ->will($this->returnCallback(function($key, $default) {
->will($this->returnValue('')); return $default;
}));
$request = new Request( $request = new Request(
[ [
@ -792,10 +798,10 @@ class RequestTest extends \Test\TestCase {
public function testGetServerProtocolWithHttpsServerValueEmpty() { public function testGetServerProtocolWithHttpsServerValueEmpty() {
$this->config $this->config
->expects($this->once())
->method('getSystemValue') ->method('getSystemValue')
->with('overwriteprotocol') ->will($this->returnCallback(function($key, $default) {
->will($this->returnValue('')); return $default;
}));
$request = new Request( $request = new Request(
[ [
@ -813,10 +819,10 @@ class RequestTest extends \Test\TestCase {
public function testGetServerProtocolDefault() { public function testGetServerProtocolDefault() {
$this->config $this->config
->expects($this->once())
->method('getSystemValue') ->method('getSystemValue')
->with('overwriteprotocol') ->will($this->returnCallback(function($key, $default) {
->will($this->returnValue('')); return $default;
}));
$request = new Request( $request = new Request(
[], [],
@ -830,15 +836,20 @@ class RequestTest extends \Test\TestCase {
public function testGetServerProtocolBehindLoadBalancers() { public function testGetServerProtocolBehindLoadBalancers() {
$this->config $this->config
->expects($this->once())
->method('getSystemValue') ->method('getSystemValue')
->with('overwriteprotocol') ->will($this->returnCallback(function($key, $default) {
->will($this->returnValue('')); if ($key === 'trusted_proxies') {
return ['1.2.3.4'];
}
return $default;
}));
$request = new Request( $request = new Request(
[ [
'server' => [ 'server' => [
'HTTP_X_FORWARDED_PROTO' => 'https,http,http' 'HTTP_X_FORWARDED_PROTO' => 'https,http,http',
'REMOTE_ADDR' => '1.2.3.4',
], ],
], ],
$this->secureRandom, $this->secureRandom,
@ -1025,12 +1036,23 @@ class RequestTest extends \Test\TestCase {
} }
public function testInsecureServerHostHttpFromForwardedHeaderSingle() { public function testInsecureServerHostHttpFromForwardedHeaderSingle() {
$this->config
->method('getSystemValue')
->will($this->returnCallback(function($key, $default) {
if ($key === 'trusted_proxies') {
return ['1.2.3.4'];
}
return $default;
}));
$request = new Request( $request = new Request(
[ [
'server' => [ 'server' => [
'SERVER_NAME' => 'from.server.name:8080', 'SERVER_NAME' => 'from.server.name:8080',
'HTTP_HOST' => 'from.host.header:8080', 'HTTP_HOST' => 'from.host.header:8080',
'HTTP_X_FORWARDED_HOST' => 'from.forwarded.host:8080', 'HTTP_X_FORWARDED_HOST' => 'from.forwarded.host:8080',
'REMOTE_ADDR' => '1.2.3.4',
] ]
], ],
$this->secureRandom, $this->secureRandom,
@ -1043,12 +1065,23 @@ class RequestTest extends \Test\TestCase {
} }
public function testInsecureServerHostHttpFromForwardedHeaderStacked() { public function testInsecureServerHostHttpFromForwardedHeaderStacked() {
$this->config
->method('getSystemValue')
->will($this->returnCallback(function($key, $default) {
if ($key === 'trusted_proxies') {
return ['1.2.3.4'];
}
return $default;
}));
$request = new Request( $request = new Request(
[ [
'server' => [ 'server' => [
'SERVER_NAME' => 'from.server.name:8080', 'SERVER_NAME' => 'from.server.name:8080',
'HTTP_HOST' => 'from.host.header:8080', 'HTTP_HOST' => 'from.host.header:8080',
'HTTP_X_FORWARDED_HOST' => 'from.forwarded.host2:8080,another.one:9000', 'HTTP_X_FORWARDED_HOST' => 'from.forwarded.host2:8080,another.one:9000',
'REMOTE_ADDR' => '1.2.3.4',
] ]
], ],
$this->secureRandom, $this->secureRandom,
@ -1062,20 +1095,16 @@ class RequestTest extends \Test\TestCase {
public function testGetServerHostWithOverwriteHost() { public function testGetServerHostWithOverwriteHost() {
$this->config $this->config
->expects($this->at(0))
->method('getSystemValue') ->method('getSystemValue')
->with('overwritehost') ->will($this->returnCallback(function($key, $default) {
->will($this->returnValue('my.overwritten.host')); if ($key === 'overwritecondaddr') {
$this->config return '';
->expects($this->at(1)) } else if ($key === 'overwritehost') {
->method('getSystemValue') return 'my.overwritten.host';
->with('overwritecondaddr') }
->will($this->returnValue(''));
$this->config return $default;
->expects($this->at(2)) }));
->method('getSystemValue')
->with('overwritehost')
->will($this->returnValue('my.overwritten.host'));
$request = new Request( $request = new Request(
[], [],
@ -1090,15 +1119,22 @@ class RequestTest extends \Test\TestCase {
public function testGetServerHostWithTrustedDomain() { public function testGetServerHostWithTrustedDomain() {
$this->config $this->config
->expects($this->at(3))
->method('getSystemValue') ->method('getSystemValue')
->with('trusted_domains') ->will($this->returnCallback(function($key, $default) {
->will($this->returnValue(['my.trusted.host'])); if ($key === 'trusted_proxies') {
return ['1.2.3.4'];
} else if ($key === 'trusted_domains') {
return ['my.trusted.host'];
}
return $default;
}));
$request = new Request( $request = new Request(
[ [
'server' => [ 'server' => [
'HTTP_X_FORWARDED_HOST' => 'my.trusted.host', 'HTTP_X_FORWARDED_HOST' => 'my.trusted.host',
'REMOTE_ADDR' => '1.2.3.4',
], ],
], ],
$this->secureRandom, $this->secureRandom,
@ -1112,20 +1148,22 @@ class RequestTest extends \Test\TestCase {
public function testGetServerHostWithUntrustedDomain() { public function testGetServerHostWithUntrustedDomain() {
$this->config $this->config
->expects($this->at(3))
->method('getSystemValue') ->method('getSystemValue')
->with('trusted_domains') ->will($this->returnCallback(function($key, $default) {
->will($this->returnValue(['my.trusted.host'])); if ($key === 'trusted_proxies') {
$this->config return ['1.2.3.4'];
->expects($this->at(4)) } else if ($key === 'trusted_domains') {
->method('getSystemValue') return ['my.trusted.host'];
->with('trusted_domains') }
->will($this->returnValue(['my.trusted.host']));
return $default;
}));
$request = new Request( $request = new Request(
[ [
'server' => [ 'server' => [
'HTTP_X_FORWARDED_HOST' => 'my.untrusted.host', 'HTTP_X_FORWARDED_HOST' => 'my.untrusted.host',
'REMOTE_ADDR' => '1.2.3.4',
], ],
], ],
$this->secureRandom, $this->secureRandom,
@ -1139,20 +1177,19 @@ class RequestTest extends \Test\TestCase {
public function testGetServerHostWithNoTrustedDomain() { public function testGetServerHostWithNoTrustedDomain() {
$this->config $this->config
->expects($this->at(3))
->method('getSystemValue') ->method('getSystemValue')
->with('trusted_domains') ->will($this->returnCallback(function($key, $default) {
->will($this->returnValue([])); if ($key === 'trusted_proxies') {
$this->config return ['1.2.3.4'];
->expects($this->at(4)) }
->method('getSystemValue') return $default;
->with('trusted_domains') }));
->will($this->returnValue([]));
$request = new Request( $request = new Request(
[ [
'server' => [ 'server' => [
'HTTP_X_FORWARDED_HOST' => 'my.untrusted.host', 'HTTP_X_FORWARDED_HOST' => 'my.untrusted.host',
'REMOTE_ADDR' => '1.2.3.4',
], ],
], ],
$this->secureRandom, $this->secureRandom,