clientIP: add trustedproxy, return first untrusted IP instead of the last one

This fixes #2828, where malicious clients passed in customized HTTP header to keep its IP address off records.

This is inspired by Sympony's Request::setTrustedProxies, but I don't want to implement everything including IP CIDR matching (IPv4 + IPv6), so I decided to reuse the local IP checker in place powered by regexp. Now admins can customize this "local" (trusted) proxy list using $conf['trustedproxy'], and by default it will allow any local IPs.

If in the future there is a need to implement array-based CIDR matching, $conf['trustedproxies'] can be used for the new config name.
This commit is contained in:
Phy 2019-10-20 20:31:40 -04:00
parent 445b937842
commit 925105e82b
4 changed files with 14 additions and 11 deletions

View File

@ -158,6 +158,9 @@ $conf['renderer_xhtml'] = 'xhtml'; //renderer to use for main page generat
$conf['readdircache'] = 0; //time cache in second for the readdir operation, 0 to deactivate.
$conf['search_nslimit'] = 0; //limit the search to the current X namespaces
$conf['search_fragment'] = 'exact'; //specify the default fragment search behavior
$conf['trustedproxy'] = '^(::1|[fF][eE]80:|127\.|10\.|192\.168\.|172\.((1[6-9])|(2[0-9])|(3[0-1]))\.)';
//Regexp of trusted proxy address when reading IP using HTTP header
// if blank, do not trust any proxy (including local IP)
/* Network Settings */
$conf['dnslookups'] = 1; //disable to disallow IP to hostname lookups

View File

@ -782,7 +782,7 @@ function checkwordblock($text = '') {
*/
function clientIP($single = false) {
/* @var Input $INPUT */
global $INPUT;
global $INPUT, $conf;
$ip = array();
$ip[] = $INPUT->server->str('REMOTE_ADDR');
@ -829,17 +829,18 @@ function clientIP($single = false) {
if(!$single) return join(',', $ip);
// decide which IP to use, trying to avoid local addresses
$ip = array_reverse($ip);
// skip trusted local addresses
foreach($ip as $i) {
if(preg_match('/^(::1|[fF][eE]80:|127\.|10\.|192\.168\.|172\.((1[6-9])|(2[0-9])|(3[0-1]))\.)/', $i)) {
if(!empty($conf['trustedproxy']) && preg_match('/'.$conf['trustedproxy'].'/', $i)) {
continue;
} else {
return $i;
}
}
// still here? just use the first (last) address
return $ip[0];
// still here? just use the last address
// this case all ips in the list are trusted
return $ip[count($ip)-1];
}
/**
@ -1523,11 +1524,7 @@ function getGoogleQuery() {
if(!preg_match('/(google|bing|yahoo|ask|duckduckgo|babylon|aol|yandex)/',$url['host'])) return '';
$query = array();
// temporary workaround against PHP bug #49733
// see http://bugs.php.net/bug.php?id=49733
if(UTF8_MBSTRING) $enc = mb_internal_encoding();
parse_str($url['query'], $query);
if(UTF8_MBSTRING) mb_internal_encoding($enc);
$q = '';
if(isset($query['q'])){
@ -1540,6 +1537,8 @@ function getGoogleQuery() {
$q = trim($q);
if(!$q) return '';
// ignore if query includes a full URL
if(strpos($q, '//') !== false) return '';
$q = preg_split('/[\s\'"\\\\`()\]\[?:!\.{};,#+*<>\\/]+/', $q, -1, PREG_SPLIT_NO_EMPTY);
return $q;
}

View File

@ -185,6 +185,7 @@ $lang['search_fragment_o_exact'] = 'exact';
$lang['search_fragment_o_starts_with'] = 'starts with';
$lang['search_fragment_o_ends_with'] = 'ends with';
$lang['search_fragment_o_contains'] = 'contains';
$lang['trustedproxy'] = 'Regexp of trusted proxy address when reading IP using HTTP header';
/* Network Options */
$lang['dnslookups'] = 'DokuWiki will lookup hostnames for remote IP addresses of users editing pages. If you have a slow or non working DNS server or don\'t want this feature, disable this option';

View File

@ -227,6 +227,7 @@ $meta['renderer_xhtml'] = array('renderer','_format' => 'xhtml','_choices' => ar
$meta['readdircache'] = array('numeric');
$meta['search_nslimit'] = array('numeric', '_min' => 0);
$meta['search_fragment'] = array('multichoice','_choices' => array('exact', 'starts_with', 'ends_with', 'contains'),);
$meta['trustedproxy'] = array('regex');
$meta['_network'] = array('fieldset');
$meta['dnslookups'] = array('onoff');
@ -237,4 +238,3 @@ $meta['proxy____user'] = array('string');
$meta['proxy____pass'] = array('password','_code' => 'base64');
$meta['proxy____ssl'] = array('onoff');
$meta['proxy____except'] = array('string');