[TikiWiki-commits] [Git][tikiwiki/tiki][29.x] [ENH] Load Balancer: Add preference to allow access from trusted IPs only
"Elifeleti Mukisa Dan \(@Danelif\) via TikiWiki-cvs" <[email protected]>
| Newsgroups | gmane.comp.cms.tiki.cvs |
|---|---|
| Message-ID | <6983146b15745_3b1844bc7661c@gitlab-sidekiq-low-urgency-cpu-bound-v2-65d8676567-gp2vg.mail> |
Elifeleti Mukisa Dan pushed to branch 29.x at Tiki Wiki CMS Groupware / Tiki Commits: fa874601 by Elifeleti Mukisa Dan at 2026-02-04T09:33:53+00:00 [ENH] Load Balancer: Add preference to allow access from trusted IPs only --- * [ENH] Load Balancer: Add preference to allow access from trusted IPs only --- * [ENH] Load Balancer: Add preference to allow access from trusted IPs only (cherry picked from commit e11fe7d02a4ff5b5ea4329feea411b856f7c8453) ae0d44be [FIX] Add preference to allow trusted Ips Co-authored-by: Elifeleti Mukisa Dan <[email protected]> See merge request tikiwiki/tiki!9484 (cherry picked from commit c9052302e524d1965bc90bd0af42033ad64ab29c) c056882e [ENH] Load Balancer: Add preference to allow access from trusted IPs only Co-authored-by: Elifeleti Mukisa Dan <[email protected]> See merge request tikiwiki/tiki!9485 - - - - - 4 changed files: - lib/prefs/feature.php - lib/tikilib.php - lib/validators/validator_uniqueemail.php - tiki-monitor.php Changes: ===================================== lib/prefs/feature.php ===================================== @@ -2597,6 +2597,23 @@ function prefs_feature_list($partial = false) 'default' => 'n', 'tags' => ['experimental'], ], + 'feature_loadbalancer_trusted_proxies' => [ + 'name' => tra('Trusted reverse proxy IPs'), + 'description' => tra('List of IP addresses of trusted reverse proxies. Only requests coming from these IPs will have their X-Forwarded-For headers trusted. Leave empty to trust all IPs (less secure).'), + 'type' => 'textarea', + 'size' => 3, + 'default' => '', + 'tags' => ['experimental'], + 'dependencies' => ['feature_loadbalancer'], + ], + 'feature_loadbalancer_header' => [ + 'name' => tra('Reverse proxy header'), + 'description' => tra('Name of the HTTP header used by your reverse proxy to pass the client IP. Common values: X-Forwarded-For, CF-Connecting-IP, X-Real-IP, X-Client-IP. There is no auto-detection.'), + 'type' => 'text', + 'default' => '', + 'tags' => ['experimental'], + 'dependencies' => ['feature_loadbalancer'], + ], 'feature_port_rewriting' => [ 'name' => tra('Tiki is behind a frontend-proxy/load-balancer that rewrites ports'), 'description' => tra('Activate this only if the server is behind a frontend-proxy/load-balancer (or reverse proxy) that rewrites ports. This enables Tiki to use the HTTP_X_FORWARDED_PROTO parameter set by the proxy, to provide correct links.'), ===================================== lib/tikilib.php ===================================== @@ -595,6 +595,7 @@ class TikiLib extends TikiDb_Bridge /*shared*/ // Returns IP address or IP address forwarded by the proxy if feature load balancer is set + // Security: Only trusts proxy headers from trusted reverse proxy IPs /** * @param $firewall true to detect ip behind a firewall * @return null|string @@ -602,35 +603,21 @@ class TikiLib extends TikiDb_Bridge public function get_ip_address($firewall = 0) { global $prefs; - if ($firewall || (isset($prefs['feature_loadbalancer']) && $prefs['feature_loadbalancer'] === "y")) { - $header_checks = [ - 'HTTP_CF_CONNECTING_IP', - 'HTTP_CLIENT_IP', - 'HTTP_PRAGMA', - 'HTTP_XONNECTION', - 'HTTP_CACHE_INFO', - 'HTTP_XPROXY', - 'HTTP_PROXY', - 'HTTP_PROXY_RENAMED', - 'HTTP_PROXY_CONNECTION', - 'HTTP_VIA', - 'HTTP_X_COMING_FROM', - 'HTTP_COMING_FROM', - 'HTTP_X_FORWARDED_FOR', - 'HTTP_X_FORWARDED', - 'HTTP_X_CLUSTER_CLIENT_IP', - 'HTTP_FORWARDED_FOR', - 'HTTP_FORWARDED', - 'HTTP_CACHE_CONTROL', - 'HTTP_X_REAL_IP', - 'REMOTE_ADDR']; - - foreach ($header_checks as $key) { - if (array_key_exists($key, $_SERVER) === true) { - foreach (explode(',', $_SERVER[$key]) as $ip) { - $ip = trim($ip); - //filter the ip with filter functions + // Check if we should trust proxy headers + $should_check_proxy_headers = $firewall || (isset($prefs['feature_loadbalancer']) && $prefs['feature_loadbalancer'] === "y"); + + if ($should_check_proxy_headers) { + // Security: Verify that the request comes from a trusted proxy + $isFromTrustedProxy = $this->isFromTrustedProxy(); + + if ($isFromTrustedProxy) { + // Determine which header to check + $header_to_check = $this->getProxyHeaderName(); + + if (! empty($header_to_check) && array_key_exists($header_to_check, $_SERVER)) { + foreach (explode(',', $_SERVER[$header_to_check]) as $ip) { + $ip = trim($ip); if (filter_var($ip, FILTER_VALIDATE_IP) !== false) { return $ip; } @@ -638,6 +625,8 @@ class TikiLib extends TikiDb_Bridge } } } + + // Fall back to REMOTE_ADDR if proxy headers are not available or not trusted if (isset($_SERVER['REMOTE_ADDR']) && filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP)) { return $_SERVER['REMOTE_ADDR']; } else { @@ -645,6 +634,57 @@ class TikiLib extends TikiDb_Bridge } } + /*shared*/ + /** + * Check if the current request comes from a trusted reverse proxy + * @return bool + */ + public function isFromTrustedProxy() + { + global $prefs; + + // If feature_loadbalancer is not enabled, don't trust proxy headers + if (! isset($prefs['feature_loadbalancer']) || $prefs['feature_loadbalancer'] !== "y") { + return false; + } + + $trusted_proxies = isset($prefs['feature_loadbalancer_trusted_proxies']) ? $prefs['feature_loadbalancer_trusted_proxies'] : ''; + $remote_addr = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : ''; + + // If no trusted proxies are configured, don't trust any proxy headers (most secure) + if (empty($trusted_proxies)) { + return false; + } + + // Parse the trusted proxies list + $trusted_ips = array_map('trim', explode(',', $trusted_proxies)); + + // Check if the request comes from a trusted proxy IP + return in_array($remote_addr, $trusted_ips, true); + } + + /*shared*/ + /** + * Get the name of the HTTP header to use for getting the client IP from a reverse proxy + * @return string|null + */ + public function getProxyHeaderName() + { + global $prefs; + + // If a specific header is configured, use it + if (isset($prefs['feature_loadbalancer_header']) && ! empty($prefs['feature_loadbalancer_header'])) { + $header = strtoupper(str_replace('-', '_', $prefs['feature_loadbalancer_header'])); + // Add HTTP_ prefix if not already present + if (strpos($header, 'HTTP_') !== 0 && $header !== 'REMOTE_ADDR') { + $header = 'HTTP_' . $header; + } + return $header; + } + + return null; + } + /*shared*/ /** * @param $user @@ -1770,7 +1810,7 @@ class TikiLib extends TikiDb_Bridge // Combined query using subqueries $query = " - SELECT + SELECT ? as lastVisit, (SELECT COUNT(*) FROM `tiki_pages` WHERE `lastModif` > ?) as pages, (SELECT COUNT(*) FROM `tiki_files` WHERE `created` > ?) as files, ===================================== lib/validators/validator_uniqueemail.php ===================================== @@ -8,17 +8,11 @@ function validator_uniqueemail($input, $parameter = '', $message = '') { global $prefs; $userlib = TikiLib::lib('user'); + $tikilib = TikiLib::lib('tiki'); include_once __DIR__ . '/../../lib/ban/banlib.php'; - $ip = $_SERVER['REMOTE_ADDR']; - - if (! empty($_SERVER['HTTP_CLIENT_IP'])) { - $ip = $_SERVER['HTTP_CLIENT_IP']; - } elseif (! empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { - $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; - } elseif ($ip == '::1') { - $ip = gethostbyname(getHostName()); - } + // Use the secure get_ip_address() method which properly validates reverse proxy headers + $ip = $tikilib->get_ip_address(); $ban_message = tra('You are not allow to do such operation, please contact the administrator.'); ===================================== tiki-monitor.php ===================================== @@ -79,26 +79,29 @@ echo $display; /** * Check monitor is restricted by IP + * Security: Uses get_ip_address() which properly validates reverse proxy headers * * @return null */ function isMonitorRestrited() { global $prefs; + $tikilib = TikiLib::lib('tiki'); $tikiMonitorRestriction = ! empty($prefs['monitor_restricted_ips']) ? explode(',', preg_replace('/\s+/', '', $prefs['monitor_restricted_ips'])) : []; - $sIpToCheck = null; - if (! empty($tikiMonitorRestriction)) { - if (! empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { - $aListIp = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); - $sIpToCheck = $aListIp[0]; - } elseif (! empty($_SERVER['REMOTE_ADDR'])) { - $sIpToCheck = $_SERVER['REMOTE_ADDR']; - } + + if (empty($tikiMonitorRestriction)) { + // No IP restrictions configured, allow access + return; } - if (in_array($sIpToCheck, $tikiMonitorRestriction) === false) { - header('location: index.php'); + // Use the secure get_ip_address() method + $sIpToCheck = $tikilib->get_ip_address(); + + if (! in_array($sIpToCheck, $tikiMonitorRestriction, true)) { + // Return 403 Forbidden instead of 302 redirect for better security semantics + http_response_code(403); + echo 'Access Forbidden: Your IP address is not authorized to access this resource.'; exit(); } } View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/fa874601206574d6554980f5d3df23040456c3e0 -- View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/fa874601206574d6554980f5d3df23040456c3e0 You're receiving this email because of your account on gitlab.com. _______________________________________________ TikiWiki-cvs mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/tikiwiki-cvs