Fix use of numeric hostname

This commit is contained in:
Laurent Destailleur 2026-07-29 12:16:48 +02:00
parent 29c85b5eed
commit ca2c725bb1
3 changed files with 95 additions and 71 deletions

View file

@ -260,6 +260,11 @@ function getURLContent($url, $postorget = 'GET', $param = '', $followlocation =
return array('http_code' => 400, 'content' => $info['content'], 'curl_error_no' => 1, 'curl_error_msg' => $info['content']);
}
/* Discard full numeric hostname */
if (preg_match('/^[x0-9a-f]+$/', $hosttocheck)) {
return array('http_code' => 400, 'content' => $info['content'], 'curl_error_no' => 1, 'curl_error_msg' => 'Host is a numeric address that is not allowed');
}
// Clean host name $hosttocheck to convert it into an IP $iptocheck
if (in_array($hosttocheck, array('localhost', 'localhost.domain'))) {
$iptocheck = '127.0.0.1';
@ -267,12 +272,15 @@ function getURLContent($url, $postorget = 'GET', $param = '', $followlocation =
$iptocheck = '::1';
} else {
// Resolve $hosttocheck to get the IP $iptocheck
// Not that a bad numeric hostname like 2130706433 will be resolved int 127.0.0.1 but
// this case is filtered previously.
$iptocheck = resolveDns($hosttocheck);
}
// Check $iptocheck is an IP (v4 or v6), if not clear value.
if (!filter_var($iptocheck, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6)) { // This is not an IP, we clean data
if (!filter_var($iptocheck, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6)) { // This is not an IP
$iptocheck = '0'; // will disabled check on IP
return array('http_code' => 400, 'content' => $info['content'], 'curl_error_no' => 1, 'curl_error_msg' => 'Host is a numeric address that is not allowed');
}
if ($iptocheck) {

View file

@ -270,4 +270,90 @@ class GetUrlLibTest extends CommonClassTest
return 1;
}
/**
* testGetURLContent
*
* @return int
*/
public function testGetURLContent()
{
global $conf;
include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
$url = 'ftp://dolibarr.org';
$tmp = getURLContent($url);
print __METHOD__." url=".$url." ".$tmp['curl_error_msg']."\n";
$tmpvar = preg_match('/not supported|disabled/', $tmp['curl_error_msg']);
$this->assertEquals(1, $tmpvar, "Did not find the /not supported|disabled/ in getURLContent error message. We should.");
$DISABLEREMOTEACCESSTODOLIBARRFR = 1;
if (empty($DISABLEREMOTEACCESSTODOLIBARRFR)) {
$url = 'https://www.dolibarr.fr'; // This is a redirect 301 page
$tmp = getURLContent($url, 'GET', '', 0); // We do NOT follow
print __METHOD__." url=".$url."\n";
$this->assertEquals(301, (empty($tmp['http_code']) ? 0 : $tmp['http_code']), 'Test getURLContent '.$url.' - Should GET url 301 response');
$url = 'https://www.dolibarr.fr'; // This is a redirect 301 page
$tmp = getURLContent($url); // We DO follow a page with return 300 so result should be 200
print __METHOD__." url=".$url."\n";
$this->assertEquals(200, (empty($tmp['http_code']) ? 0 : $tmp['http_code']), 'Should GET url 301 with a follow -> 200 but we get '.(empty($tmp['http_code']) ? 0 : $tmp['http_code']));
}
$url = 'http://localhost';
$tmp = getURLContent($url, 'GET', '', 0, array(), array('http', 'https'), 0); // Only external URL
print __METHOD__." url=".$url."\n";
$this->assertEquals(400, (empty($tmp['http_code']) ? 0 : $tmp['http_code']), 'Should GET url to '.$url.' that resolves to a local URL'); // Test we receive an error because localtest.me is not an external URL
$url = 'http://127.0.0.1';
$tmp = getURLContent($url, 'GET', '', 0, array(), array('http', 'https'), 0); // Only external URL
print __METHOD__." url=".$url."\n";
$this->assertEquals(400, (empty($tmp['http_code']) ? 0 : $tmp['http_code']), 'Should GET url to '.$url.' that is a local URL'); // Test we receive an error because 127.0.0.1 is not an external URL
$url = 'http://127.0.2.1';
$tmp = getURLContent($url, 'GET', '', 0, array(), array('http', 'https'), 0); // Only external URL
print __METHOD__." url=".$url."\n";
$this->assertEquals(400, (empty($tmp['http_code']) ? 0 : $tmp['http_code']), 'Should GET url to '.$url.' that is a local URL'); // Test we receive an error because 127.0.2.1 is not an external URL
$url = 'https://169.254.0.1';
$tmp = getURLContent($url, 'GET', '', 0, array(), array('http', 'https'), 0); // Only external URL
print __METHOD__." url=".$url."\n";
$this->assertEquals(400, (empty($tmp['http_code']) ? 0 : $tmp['http_code']), 'Should GET url to '.$url.' that is a local URL'); // Test we receive an error because 169.254.0.1 is not an external URL
$url = 'http://[::1]';
$tmp = getURLContent($url, 'GET', '', 0, array(), array('http', 'https'), 0); // Only external URL
print __METHOD__." url=".$url."\n";
$this->assertEquals(400, (empty($tmp['http_code']) ? 0 : $tmp['http_code']), 'Should GET url to '.$url.' that is a local URL'); // Test we receive an error because [::1] is not an external URL
/*$url = 'localtest.me';
$tmp = getURLContent($url, 'GET', '', 0, array(), array('http', 'https'), 0); // Only external URL
print __METHOD__." url=".$url."\n";
$this->assertEquals(400, (empty($tmp['http_code']) ? 0 : $tmp['http_code']), 'Should GET url to '.$url.' that resolves to a local URL'); // Test we receive an error because localtest.me is not an external URL
*/
$url = 'http://192.0.0.192';
$tmp = getURLContent($url, 'GET', '', 0, array(), array('http', 'https'), 0); // Only external URL but on an IP in blacklist
print __METHOD__." url=".$url." tmp['http_code'] = ".(empty($tmp['http_code']) ? 0 : $tmp['http_code'])."\n";
$this->assertEquals(400, (empty($tmp['http_code']) ? 0 : $tmp['http_code']), 'Access should be refused and was not'); // Test we receive an error because ip is in blacklist
$url = 'https://2130706433';
$tmp = getURLContent($url, 'GET', '', 0, array(), array('http', 'https'), 0); // Only external URL
print __METHOD__." url=".$url."\n";
$this->assertEquals("Host is a numeric address that is not allowed", (empty($tmp['curl_error_msg']) ? "" : $tmp['curl_error_msg']), 'Should GET error Not a valid ip address'); // Test we receive an error because 169.254.0.1 is not an external URL
$url = 'https://0x7f000001';
$tmp = getURLContent($url, 'GET', '', 0, array(), array('http', 'https'), 0); // Only external URL
print __METHOD__." url=".$url."\n";
$this->assertEquals("Host is a numeric address that is not allowed", (empty($tmp['curl_error_msg']) ? "" : $tmp['curl_error_msg']), 'Should GET error Not a valid ip address'); // Test we receive an error because 169.254.0.1 is not an external URL
$url = 'https://017700000001';
$tmp = getURLContent($url, 'GET', '', 0, array(), array('http', 'https'), 0); // Only external URL
print __METHOD__." url=".$url."\n";
$this->assertEquals("Host is a numeric address that is not allowed", (empty($tmp['curl_error_msg']) ? "" : $tmp['curl_error_msg']), 'Should GET error Not a valid ip address'); // Test we receive an error because 169.254.0.1 is not an external URL
return 0;
}
}

View file

@ -482,76 +482,6 @@ class SecurityTest extends CommonClassTest
}
/**
* testGetRandomPassword
*
* @return int
*/
public function testGetURLContent()
{
global $conf;
include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
$url = 'ftp://mydomain.com';
$tmp = getURLContent($url);
print __METHOD__." url=".$url." ".$tmp['curl_error_msg']."\n";
$tmpvar = preg_match('/not supported|disabled/', $tmp['curl_error_msg']);
$this->assertEquals(1, $tmpvar, "Did not find the /not supported|disabled/ in getURLContent error message. We should.");
$DISABLEREMOTEACCESSTODOLIBARRFR = 1;
if (empty($DISABLEREMOTEACCESSTODOLIBARRFR)) {
$url = 'https://www.dolibarr.fr'; // This is a redirect 301 page
$tmp = getURLContent($url, 'GET', '', 0); // We do NOT follow
print __METHOD__." url=".$url."\n";
$this->assertEquals(301, (empty($tmp['http_code']) ? 0 : $tmp['http_code']), 'Test getURLContent '.$url.' - Should GET url 301 response');
$url = 'https://www.dolibarr.fr'; // This is a redirect 301 page
$tmp = getURLContent($url); // We DO follow a page with return 300 so result should be 200
print __METHOD__." url=".$url."\n";
$this->assertEquals(200, (empty($tmp['http_code']) ? 0 : $tmp['http_code']), 'Should GET url 301 with a follow -> 200 but we get '.(empty($tmp['http_code']) ? 0 : $tmp['http_code']));
}
$url = 'http://localhost';
$tmp = getURLContent($url, 'GET', '', 0, array(), array('http', 'https'), 0); // Only external URL
print __METHOD__." url=".$url."\n";
$this->assertEquals(400, (empty($tmp['http_code']) ? 0 : $tmp['http_code']), 'Should GET url to '.$url.' that resolves to a local URL'); // Test we receive an error because localtest.me is not an external URL
$url = 'http://127.0.0.1';
$tmp = getURLContent($url, 'GET', '', 0, array(), array('http', 'https'), 0); // Only external URL
print __METHOD__." url=".$url."\n";
$this->assertEquals(400, (empty($tmp['http_code']) ? 0 : $tmp['http_code']), 'Should GET url to '.$url.' that is a local URL'); // Test we receive an error because 127.0.0.1 is not an external URL
$url = 'http://127.0.2.1';
$tmp = getURLContent($url, 'GET', '', 0, array(), array('http', 'https'), 0); // Only external URL
print __METHOD__." url=".$url."\n";
$this->assertEquals(400, (empty($tmp['http_code']) ? 0 : $tmp['http_code']), 'Should GET url to '.$url.' that is a local URL'); // Test we receive an error because 127.0.2.1 is not an external URL
$url = 'https://169.254.0.1';
$tmp = getURLContent($url, 'GET', '', 0, array(), array('http', 'https'), 0); // Only external URL
print __METHOD__." url=".$url."\n";
$this->assertEquals(400, (empty($tmp['http_code']) ? 0 : $tmp['http_code']), 'Should GET url to '.$url.' that is a local URL'); // Test we receive an error because 169.254.0.1 is not an external URL
$url = 'http://[::1]';
$tmp = getURLContent($url, 'GET', '', 0, array(), array('http', 'https'), 0); // Only external URL
print __METHOD__." url=".$url."\n";
$this->assertEquals(400, (empty($tmp['http_code']) ? 0 : $tmp['http_code']), 'Should GET url to '.$url.' that is a local URL'); // Test we receive an error because [::1] is not an external URL
/*$url = 'localtest.me';
$tmp = getURLContent($url, 'GET', '', 0, array(), array('http', 'https'), 0); // Only external URL
print __METHOD__." url=".$url."\n";
$this->assertEquals(400, (empty($tmp['http_code']) ? 0 : $tmp['http_code']), 'Should GET url to '.$url.' that resolves to a local URL'); // Test we receive an error because localtest.me is not an external URL
*/
$url = 'http://192.0.0.192';
$tmp = getURLContent($url, 'GET', '', 0, array(), array('http', 'https'), 0); // Only external URL but on an IP in blacklist
print __METHOD__." url=".$url." tmp['http_code'] = ".(empty($tmp['http_code']) ? 0 : $tmp['http_code'])."\n";
$this->assertEquals(400, (empty($tmp['http_code']) ? 0 : $tmp['http_code']), 'Access should be refused and was not'); // Test we receive an error because ip is in blacklist
return 0;
}
/**
* testDolSanitizeUrl
*