From 4f00b49babd8e7fc8aaaa7ab0da6e5fa842cd7e6 Mon Sep 17 00:00:00 2001 From: ldestailleur Date: Fri, 28 Mar 2025 21:02:27 +0100 Subject: [PATCH] Fix reliability of ip in logs --- htdocs/core/lib/functions.lib.php | 83 +++++++++++++++++++++++++------ 1 file changed, 69 insertions(+), 14 deletions(-) diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index 17355e38a10..05e317ef11a 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -2446,14 +2446,57 @@ function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = 'ospid' => getmypid() // on linux, max value is defined into cat /proc/sys/kernel/pid_max ); - $remoteip = getUserRemoteIP(); // Get ip when page run on a web server + // For log, we want the reliable IP first. + $remoteip = getUserRemoteIP(1); // Get ip when page run on a web server if (!empty($remoteip)) { $data['ip'] = $remoteip; // This is when server run behind a reverse proxy - if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != $remoteip) { - $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].' -> '.$data['ip']; - } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != $remoteip) { - $data['ip'] = $_SERVER['HTTP_CLIENT_IP'].' -> '.$data['ip']; + // A HTTP_X_FORWARDED_FOR as format "ip real of user, ip of proxy1, ip of proxy2, ..." + // $data['ip'] is last + if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { + $tmpips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); + $data['ip'] = ''; + $foundremoteip = 0; + $j = 0; + foreach ($tmpips as $tmpip) { + $tmpip = trim($tmpip); + if (strtolower($tmpip) == strtolower($remoteip)) { + $foundremoteip = 1; + } + if (empty($data['ip'])) { + $data['ip'] = $tmpip; + } else { + $j++; + $data['ip'] .= (($j == 1) ? ' [via ' : ',').$tmpip; + } + } + if (!$foundremoteip) { + $j++; + $data['ip'] .= (($j == 1) ? ' [via ' : ',').$remoteip; + } + $data['ip'] .= (($j > 0) ? ']' : ''); + } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) ) { + $tmpips = explode(',', $_SERVER['HTTP_CLIENT_IP']); + $data['ip'] = ''; + $foundremoteip = 0; + $j = 0; + foreach ($tmpips as $tmpip) { + $tmpip = trim($tmpip); + if (strtolower($tmpip) == strtolower($remoteip)) { + $foundremoteip = 1; + } + if (empty($data['ip'])) { + $data['ip'] = $tmpip; + } else { + $j++; + $data['ip'] .= (($j == 1) ? ' [via ' : ',').$tmpip; + } + } + if (!$foundremoteip) { + $j++; + $data['ip'] .= (($j == 1) ? ' [via ' : ',').$remoteip; + } + $data['ip'] .= (($j > 0) ? ']' : ''); } } elseif (!empty($_SERVER['SERVER_ADDR'])) { // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache) @@ -4635,27 +4678,39 @@ function dol_print_ip($ip, $mode = 0) } /** - * Return the IP of remote user. + * Return the real IP of remote user. * Take HTTP_X_FORWARDED_FOR (defined when using proxy) * Then HTTP_CLIENT_IP if defined (rare) - * Then REMOTE_ADDR (no way to be modified by user but may be wrong if user is using a proxy) + * Then REMOTE_ADDR (the last seerver ip in proxy chain). + * REMOTE_ADDR can't be modified by client and may be the IP of a proxy. + * Note: that if Apache module "remoteip" module is on, $_SERVER["REMOTE_ADDR"] may be replaced y HTTP_X_FORWARDED_FOR directly + * from CF-Connecting-IP, but only if remote is in trusted cloudflare list. * - * @return string Ip of remote user. + * @param int $trusted 0=Default, 1=Trusted value (the last IP that was not altered by client) + * @return string Real IP of remote user. */ -function getUserRemoteIP() +function getUserRemoteIP($trusted = 0) { - if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) { - if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_CLIENT_IP'])) { + if ($trusted) { // Return only IP we can rely on (not spoofable by the client) + $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may be the IP of a proxy + // Note that if apache module remoteip has been enabled, REMOTE_ADDR can contain the real client (the value from cloudFlare HTTP_CF_CONNECTING_IP for example) + // if the proxy were added in the list of trusted proxy. + return $ip; + } + + // Try to guess the real IP of client (but this may not be reliable) + if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]\s]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) { + if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]\s]/', $_SERVER['HTTP_CLIENT_IP'])) { if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) { - $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may have been the IP of the proxy and not the client + $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may be the IP of the proxy and not the client } else { $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client } } else { - $ip = $_SERVER['HTTP_CLIENT_IP']; // value is clean here but may have been forged by proxy + $ip = preg_replace('/,.*$/', '', $_SERVER['HTTP_CLIENT_IP']); // value is clean here but may have been forged by proxy } } else { - $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; // value is clean here but may have been forged by proxy + $ip = preg_replace('/,.*$/', '', $_SERVER['HTTP_X_FORWARDED_FOR']); // value is clean here but may have been forged by proxy } return $ip; }