Skip to content

Commit f30d23b

Browse files
authored
Merge pull request #32615 from sleiner/feature/ipv6-cidr-for-trusted-proxies
Support specifying IPv6 proxies in CIDR notation
2 parents f421c7a + 09362ea commit f30d23b

File tree

2 files changed

+80
-30
lines changed

2 files changed

+80
-30
lines changed

lib/private/AppFramework/Http/Request.php

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
* @author Thomas Müller <thomas.mueller@tmit.eu>
2626
* @author Thomas Tanghus <thomas@tanghus.net>
2727
* @author Vincent Petry <vincent@nextcloud.com>
28+
* @author Simon Leiner <simon@leiner.me>
2829
*
2930
* @license AGPL-3.0
3031
*
@@ -50,6 +51,7 @@
5051
use OCP\IRequest;
5152
use OCP\IRequestId;
5253
use OCP\Security\ICrypto;
54+
use Symfony\Component\HttpFoundation\IpUtils;
5355

5456
/**
5557
* Class for accessing variables in the request.
@@ -572,42 +574,13 @@ public function getId(): string {
572574
return $this->requestId->getId();
573575
}
574576

575-
/**
576-
* Checks if given $remoteAddress matches given $trustedProxy.
577-
* If $trustedProxy is an IPv4 IP range given in CIDR notation, true will be returned if
578-
* $remoteAddress is an IPv4 address within that IP range.
579-
* Otherwise $remoteAddress will be compared to $trustedProxy literally and the result
580-
* will be returned.
581-
* @return boolean true if $remoteAddress matches $trustedProxy, false otherwise
582-
*/
583-
protected function matchesTrustedProxy($trustedProxy, $remoteAddress) {
584-
$cidrre = '/^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\/([0-9]{1,2})$/';
585-
586-
if (preg_match($cidrre, $trustedProxy, $match)) {
587-
$net = $match[1];
588-
$shiftbits = min(32, max(0, 32 - intval($match[2])));
589-
$netnum = ip2long($net) >> $shiftbits;
590-
$ipnum = ip2long($remoteAddress) >> $shiftbits;
591-
592-
return $ipnum === $netnum;
593-
}
594-
595-
return $trustedProxy === $remoteAddress;
596-
}
597-
598577
/**
599578
* Checks if given $remoteAddress matches any entry in the given array $trustedProxies.
600579
* For details regarding what "match" means, refer to `matchesTrustedProxy`.
601580
* @return boolean true if $remoteAddress matches any entry in $trustedProxies, false otherwise
602581
*/
603582
protected function isTrustedProxy($trustedProxies, $remoteAddress) {
604-
foreach ($trustedProxies as $tp) {
605-
if ($this->matchesTrustedProxy($tp, $remoteAddress)) {
606-
return true;
607-
}
608-
}
609-
610-
return false;
583+
return IpUtils::checkIp($remoteAddress, $trustedProxies);
611584
}
612585

613586
/**

tests/lib/AppFramework/Http/RequestTest.php

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,83 @@ public function testGetRemoteAddressWithNotMatchingCidrTrustedRemote() {
585585
$this->assertSame('192.168.3.99', $request->getRemoteAddress());
586586
}
587587

588+
public function testGetRemoteIpv6AddressWithMatchingIpv6CidrTrustedRemote() {
589+
$this->config
590+
->expects($this->exactly(2))
591+
->method('getSystemValue')
592+
->withConsecutive(
593+
['trusted_proxies'],
594+
['forwarded_for_headers']
595+
)->willReturnOnConsecutiveCalls(
596+
['2001:db8:85a3:8d3:1319:8a20::/95'],
597+
['HTTP_X_FORWARDED_FOR']
598+
);
599+
600+
$request = new Request(
601+
[
602+
'server' => [
603+
'REMOTE_ADDR' => '2001:db8:85a3:8d3:1319:8a21:370:7348',
604+
'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4',
605+
'HTTP_X_FORWARDED_FOR' => '192.168.0.233'
606+
],
607+
],
608+
$this->requestId,
609+
$this->config,
610+
$this->csrfTokenManager,
611+
$this->stream
612+
);
613+
614+
$this->assertSame('192.168.0.233', $request->getRemoteAddress());
615+
}
616+
617+
public function testGetRemoteAddressIpv6WithNotMatchingCidrTrustedRemote() {
618+
$this->config
619+
->expects($this->once())
620+
->method('getSystemValue')
621+
->with('trusted_proxies')
622+
->willReturn(['fd::/8']);
623+
624+
$request = new Request(
625+
[
626+
'server' => [
627+
'REMOTE_ADDR' => '2001:db8:85a3:8d3:1319:8a2e:370:7348',
628+
'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4',
629+
'HTTP_X_FORWARDED_FOR' => '192.168.0.233'
630+
],
631+
],
632+
$this->requestId,
633+
$this->config,
634+
$this->csrfTokenManager,
635+
$this->stream
636+
);
637+
638+
$this->assertSame('2001:db8:85a3:8d3:1319:8a2e:370:7348', $request->getRemoteAddress());
639+
}
640+
641+
public function testGetRemoteAddressIpv6WithInvalidTrustedProxy() {
642+
$this->config
643+
->expects($this->once())
644+
->method('getSystemValue')
645+
->with('trusted_proxies')
646+
->willReturn(['fx::/8']);
647+
648+
$request = new Request(
649+
[
650+
'server' => [
651+
'REMOTE_ADDR' => '2001:db8:85a3:8d3:1319:8a2e:370:7348',
652+
'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4',
653+
'HTTP_X_FORWARDED_FOR' => '192.168.0.233'
654+
],
655+
],
656+
$this->requestId,
657+
$this->config,
658+
$this->csrfTokenManager,
659+
$this->stream
660+
);
661+
662+
$this->assertSame('2001:db8:85a3:8d3:1319:8a2e:370:7348', $request->getRemoteAddress());
663+
}
664+
588665
public function testGetRemoteAddressWithXForwardedForIPv6() {
589666
$this->config
590667
->expects($this->exactly(2))

0 commit comments

Comments
 (0)