Browse Source

Merge a50ec15000 into 6bd729b2f2

pull/9867/merge
c4539 1 day ago
committed by GitHub
parent
commit
0266153c01
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 81
      program/lib/Roundcube/rcube_utils.php

81
program/lib/Roundcube/rcube_utils.php

@ -787,7 +787,7 @@ class rcube_utils
*/
public static function check_proxy_whitelist_ip()
{
return in_array($_SERVER['REMOTE_ADDR'], (array) rcube::get_instance()->config->get('proxy_whitelist', []));
return self::isWhitelistedProxy($_SERVER['REMOTE_ADDR'], (array) rcube::get_instance()->config->get('proxy_whitelist', []));
}
/**
@ -944,6 +944,81 @@ class rcube_utils
return $address;
}
/**
* Checks if $ip is listed (exactly) or falls into any CIDR subnet in $whitelist.
* Supports IPv4 and IPv6.
*
* @param string $ip
* @param array $whitelist list of IPs or CIDR ranges, e.g. ['10.0.0.5','10.42.0.0/16']
* @return bool
*/
private static function isWhitelistedProxy(string $ip, array $whitelist): bool
{
foreach ($whitelist as $entry) {
if (self::matchIpOrCidr($ip, $entry)) {
return true;
}
}
return false;
}
/**
* Match a single IPv4 or IPv6 address against an IP or CIDR subnet.
*
* @param string $ip IPv4 or IPv6 address
* @param string $pattern either a single IP or CIDR (e.g. '192.168.1.0/24')
* @return bool
*/
private static function matchIpOrCidr(string $ip, string $pattern): bool
{
// if no prefix, do a straight compare (case‐insensitive)
if (strpos($pattern, '/') === false) {
return strcasecmp($ip, $pattern) === 0;
}
list($subnet, $prefix) = explode('/', $pattern, 2);
$prefix = (int) $prefix;
// convert to binary form (4 bytes for IPv4, 16 for IPv6)
$ipBin = @inet_pton($ip);
$subnetBin = @inet_pton($subnet);
if ($ipBin === false || $subnetBin === false) {
return false;
}
// must be same address family
$len = strlen($ipBin);
if ($len !== strlen($subnetBin)) {
return false;
}
// prefix must be 0–(len*8)
$maxBits = $len * 8;
if ($prefix < 0 || $prefix > $maxBits) {
return false;
}
// build the mask: sequence of 0xFF bytes, then one partial byte, then 0x00 bytes
$mask = '';
$bitsRemaining = $prefix;
for ($i = 0; $i < $len; $i++) {
if ($bitsRemaining >= 8) {
$mask .= "\xFF";
$bitsRemaining -= 8;
}
elseif ($bitsRemaining > 0) {
$mask .= chr((0xFF << (8 - $bitsRemaining)) & 0xFF);
$bitsRemaining = 0;
}
else {
$mask .= "\x00";
}
}
// compare network bits
return (($ipBin & $mask) === ($subnetBin & $mask));
}
/**
* Returns the real remote IP address
*
@ -954,11 +1029,11 @@ class rcube_utils
// Check if any of the headers are set first to improve performance
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) || !empty($_SERVER['HTTP_X_REAL_IP'])) {
$proxy_whitelist = (array) rcube::get_instance()->config->get('proxy_whitelist', []);
if (in_array($_SERVER['REMOTE_ADDR'], $proxy_whitelist)) {
if (self::isWhitelistedProxy($_SERVER['REMOTE_ADDR'], $proxy_whitelist)) {
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
foreach (array_reverse(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])) as $forwarded_ip) {
$forwarded_ip = trim($forwarded_ip);
if (!in_array($forwarded_ip, $proxy_whitelist)) {
if (!self::isWhitelistedProxy($forwarded_ip, $proxy_whitelist)) {
return $forwarded_ip;
}
}

Loading…
Cancel
Save