[TikiWiki-commits] [Git][tikiwiki/tiki][master] [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 <69830fe45c5ed_3b184304314d2@gitlab-sidekiq-low-urgency-cpu-bound-v2-65d8676567-42nfb.mail>

Elifeleti Mukisa Dan pushed to branch master at Tiki Wiki CMS Groupware / Tiki


Commits:
c9052302 by Elifeleti Mukisa Dan at 2026-02-04T09:13:23+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


(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

- - - - -


4 changed files:

- lib/prefs/feature.php
- lib/tikilib.php
- lib/validators/validator_uniqueemail.php
- tiki-monitor.php


Changes:

=====================================
lib/prefs/feature.php
=====================================
@@ -2615,6 +2615,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
=====================================
@@ -597,6 +597,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
@@ -604,35 +605,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;
                         }
@@ -640,6 +627,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 {
@@ -647,6 +636,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
@@ -1776,7 +1816,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 json_encode($result);
 
 /**
  * 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/c9052302e524d1965bc90bd0af42033ad64ab29c

-- 
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/c9052302e524d1965bc90bd0af42033ad64ab29c
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.