Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 3 commits into from
Oct 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
95 changes: 75 additions & 20 deletions _test/tests/inc/common_clientip.test.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,148 +2,203 @@

class common_clientIP_test extends DokuWikiTest {

function setup(){
parent::setup();

global $conf;
$conf['trustedproxy'] = '^(::1|[fF][eE]80:|127\.|10\.|192\.168\.|172\.((1[6-9])|(2[0-9])|(3[0-1]))\.)';
}

function test_simple_all(){
$_SERVER['REMOTE_ADDR'] = '123.123.123.123';
$_SERVER['HTTP_X_REAL_IP'] = '';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '';
$out = '123.123.123.123';
$this->assertEquals(clientIP(),$out);
$this->assertEquals($out, clientIP());
}

function test_proxy1_all(){
$_SERVER['REMOTE_ADDR'] = '123.123.123.123';
$_SERVER['HTTP_X_REAL_IP'] = '77.77.77.77';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '';
$out = '123.123.123.123,77.77.77.77';
$this->assertEquals(clientIP(),$out);
$this->assertEquals($out, clientIP());
}

function test_proxy2_all(){
$_SERVER['REMOTE_ADDR'] = '123.123.123.123';
$_SERVER['HTTP_X_REAL_IP'] = '';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '77.77.77.77';
$out = '123.123.123.123,77.77.77.77';
$this->assertEquals(clientIP(),$out);
$this->assertEquals($out, clientIP());
}

function test_proxyhops_all(){
$_SERVER['REMOTE_ADDR'] = '123.123.123.123';
$_SERVER['HTTP_X_REAL_IP'] = '';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '77.77.77.77,66.66.66.66';
$out = '123.123.123.123,77.77.77.77,66.66.66.66';
$this->assertEquals(clientIP(),$out);
$this->assertEquals($out, clientIP());
}

function test_simple_single(){
$_SERVER['REMOTE_ADDR'] = '123.123.123.123';
$_SERVER['HTTP_X_REAL_IP'] = '';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '';
$out = '123.123.123.123';
$this->assertEquals(clientIP(true),$out);
$this->assertEquals($out, clientIP(true));
}

function test_proxy1_single(){
$_SERVER['REMOTE_ADDR'] = '123.123.123.123';
$_SERVER['HTTP_X_REAL_IP'] = '77.77.77.77';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '';
$out = '77.77.77.77';
$this->assertEquals(clientIP(true),$out);
$out = '123.123.123.123';
$this->assertEquals($out, clientIP(true));
}

function test_proxy2_single(){
$_SERVER['REMOTE_ADDR'] = '123.123.123.123';
$_SERVER['HTTP_X_REAL_IP'] = '';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '77.77.77.77';
$out = '77.77.77.77';
$this->assertEquals(clientIP(true),$out);
$out = '123.123.123.123';
$this->assertEquals($out, clientIP(true));
}

function test_proxyhops_single(){
$_SERVER['REMOTE_ADDR'] = '123.123.123.123';
$_SERVER['HTTP_X_REAL_IP'] = '';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '77.77.77.77,66.66.66.66';
$out = '123.123.123.123';
$this->assertEquals($out, clientIP(true));
}

function test_proxy1_local_single(){
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$_SERVER['HTTP_X_REAL_IP'] = '77.77.77.77';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '';
$out = '77.77.77.77';
$this->assertEquals($out, clientIP(true));
}

function test_proxy2_local_single(){
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$_SERVER['HTTP_X_REAL_IP'] = '';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '77.77.77.77';
$out = '77.77.77.77';
$this->assertEquals($out, clientIP(true));
}

function test_proxyhops1_local_single(){
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$_SERVER['HTTP_X_REAL_IP'] = '';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '77.77.77.77,66.66.66.66';
$out = '77.77.77.77';
$this->assertEquals($out, clientIP(true));
}

function test_proxyhops2_local_single(){
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$_SERVER['HTTP_X_REAL_IP'] = '';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '10.0.0.1,66.66.66.66';
$out = '66.66.66.66';
$this->assertEquals(clientIP(true),$out);
$this->assertEquals($out, clientIP(true));
}

function test_local_all(){
$_SERVER['REMOTE_ADDR'] = '123.123.123.123';
$_SERVER['HTTP_X_REAL_IP'] = '';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '127.0.0.1';
$out = '123.123.123.123,127.0.0.1';
$this->assertEquals(clientIP(),$out);
$this->assertEquals($out, clientIP());
}

function test_local1_single(){
$_SERVER['REMOTE_ADDR'] = '123.123.123.123';
$_SERVER['HTTP_X_REAL_IP'] = '';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '127.0.0.1';
$out = '123.123.123.123';
$this->assertEquals(clientIP(true),$out);
$this->assertEquals($out, clientIP(true));
}

function test_local2_single(){
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$_SERVER['HTTP_X_REAL_IP'] = '';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '123.123.123.123';
$out = '123.123.123.123';
$this->assertEquals(clientIP(true),$out);
$this->assertEquals($out, clientIP(true));
}

function test_local3_single(){
$_SERVER['REMOTE_ADDR'] = '123.123.123.123';
$_SERVER['HTTP_X_REAL_IP'] = '';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '127.0.0.1,10.0.0.1,192.168.0.2,172.17.1.1,172.21.1.1,172.31.1.1';
$out = '123.123.123.123';
$this->assertEquals(clientIP(true),$out);
$this->assertEquals($out, clientIP(true));
}

function test_local4_single(){
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$_SERVER['HTTP_X_REAL_IP'] = '';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '192.168.0.5';
$out = '192.168.0.5';
$this->assertEquals(clientIP(true),$out);
$this->assertEquals($out, clientIP(true));
}

function test_garbage_all(){
$_SERVER['REMOTE_ADDR'] = '123.123.123.123';
$_SERVER['HTTP_X_REAL_IP'] = '';
$_SERVER['HTTP_X_FORWARDED_FOR'] = 'some garbage, or something, 222';
$out = '123.123.123.123';
$this->assertEquals(clientIP(),$out);
$this->assertEquals($out, clientIP());
}

function test_garbage_single(){
$_SERVER['REMOTE_ADDR'] = '123.123.123.123';
$_SERVER['HTTP_X_REAL_IP'] = '';
$_SERVER['HTTP_X_FORWARDED_FOR'] = 'some garbage, or something, 222';
$out = '123.123.123.123';
$this->assertEquals(clientIP(true),$out);
$this->assertEquals($out, clientIP(true));
}

function test_garbageonly_all(){
$_SERVER['REMOTE_ADDR'] = 'argh';
$_SERVER['HTTP_X_REAL_IP'] = '';
$_SERVER['HTTP_X_FORWARDED_FOR'] = 'some garbage, or something, 222';
$out = '0.0.0.0';
$this->assertEquals(clientIP(),$out);
$this->assertEquals($out, clientIP());
}

function test_garbageonly_single(){
$_SERVER['REMOTE_ADDR'] = 'argh';
$_SERVER['HTTP_X_REAL_IP'] = '';
$_SERVER['HTTP_X_FORWARDED_FOR'] = 'some garbage, or something, 222';
$out = '0.0.0.0';
$this->assertEquals(clientIP(true),$out);
$this->assertEquals($out, clientIP(true));
}

function test_malicious(){
$_SERVER['REMOTE_ADDR'] = '';
$_SERVER['HTTP_X_REAL_IP'] = '';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '<?php set_time_limit(0);echo \'my_delim\';passthru(123.123.123.123);die;?>';
$out = '0.0.0.0';
$this->assertEquals(clientIP(),$out);
$this->assertEquals($out, clientIP());
}

function test_malicious_with_remote_addr(){
$_SERVER['REMOTE_ADDR'] = '8.8.8.8';
$_SERVER['HTTP_X_REAL_IP'] = '';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '<?php set_time_limit(0);echo \'my_delim\';passthru(\',123.123.123.123,\');die;?>';
$out = '8.8.8.8';
$this->assertEquals($out, clientIP(true));
}

function test_proxied_malicious_with_remote_addr(){
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$_SERVER['HTTP_X_REAL_IP'] = '';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '8.8.8.8,<?php set_time_limit(0);echo \'my_delim\';passthru(\',123.123.123.123,\');die;?>';
$out = '127.0.0.1,8.8.8.8,123.123.123.123';
$this->assertEquals($out, clientIP());
}

}
Expand Down
3 changes: 3 additions & 0 deletions conf/dokuwiki.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@
$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
Expand Down
19 changes: 9 additions & 10 deletions inc/common.php
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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];
}

/**
Expand Down Expand Up @@ -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'])){
Expand All @@ -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;
}
Expand Down
1 change: 1 addition & 0 deletions lib/plugins/config/lang/en/lang.php
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@
$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';
Expand Down
2 changes: 1 addition & 1 deletion lib/plugins/config/settings/config.metadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@
$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');
Expand All @@ -237,4 +238,3 @@
$meta['proxy____pass'] = array('password','_code' => 'base64');
$meta['proxy____ssl'] = array('onoff');
$meta['proxy____except'] = array('string');