diff --git a/htdocs/admin/system/filecheck.php b/htdocs/admin/system/filecheck.php
index a3d54104a02..c8878b6b69f 100644
--- a/htdocs/admin/system/filecheck.php
+++ b/htdocs/admin/system/filecheck.php
@@ -147,7 +147,7 @@ if (GETPOST('target') == 'remote')
$xmlarray = getURLContent($xmlremote);
// Return array('content'=>response,'curl_error_no'=>errno,'curl_error_msg'=>errmsg...)
- if (!$xmlarray['curl_error_no'] && $xmlarray['http_code'] != '404')
+ if (!$xmlarray['curl_error_no'] && $xmlarray['http_code'] != '400' && $xmlarray['http_code'] != '404')
{
$xmlfile = $xmlarray['content'];
//print "xmlfilestart".$xmlfile."xmlfileend";
diff --git a/htdocs/admin/system/perf.php b/htdocs/admin/system/perf.php
index 7857773924f..d9901818c92 100644
--- a/htdocs/admin/system/perf.php
+++ b/htdocs/admin/system/perf.php
@@ -431,7 +431,6 @@ print '
';
print '';
print $form->textwithpicto($langs->trans("CompressionOfResources"), $langs->trans("CompressionOfResourcesDesc"));
print ': ';
-//$tmp=getURLContent(DOL_URL_ROOT.'/index.php','GET');var_dump($tmp);
print '
';
// on PHP
print '
'.img_picto('', 'tick.png').' '.$langs->trans("FilesOfTypeCompressed", 'php (.php)').'
';
diff --git a/htdocs/admin/tools/update.php b/htdocs/admin/tools/update.php
index 27b53e8266e..f39766818f3 100644
--- a/htdocs/admin/tools/update.php
+++ b/htdocs/admin/tools/update.php
@@ -54,7 +54,7 @@ $version = '0.0';
if ($action == 'getlastversion')
{
- $result = getURLContent('http://sourceforge.net/projects/dolibarr/rss');
+ $result = getURLContent('https://sourceforge.net/projects/dolibarr/rss');
//var_dump($result['content']);
$sfurl = simplexml_load_string($result['content']);
}
diff --git a/htdocs/api/class/api_setup.class.php b/htdocs/api/class/api_setup.class.php
index 8177fbf7e1e..8e632fa0032 100644
--- a/htdocs/api/class/api_setup.class.php
+++ b/htdocs/api/class/api_setup.class.php
@@ -1634,7 +1634,7 @@ class Setup extends DolibarrApi
$xmlarray = getURLContent($xmlremote);
// Return array('content'=>response,'curl_error_no'=>errno,'curl_error_msg'=>errmsg...)
- if (!$xmlarray['curl_error_no'] && $xmlarray['http_code'] != '404')
+ if (!$xmlarray['curl_error_no'] && $xmlarray['http_code'] != '400' && $xmlarray['http_code'] != '404')
{
$xmlfile = $xmlarray['content'];
//print "xmlfilestart".$xmlfile."endxmlfile";
diff --git a/htdocs/core/lib/geturl.lib.php b/htdocs/core/lib/geturl.lib.php
index 908884ee059..9a4ac16e2f2 100644
--- a/htdocs/core/lib/geturl.lib.php
+++ b/htdocs/core/lib/geturl.lib.php
@@ -18,11 +18,13 @@
/**
* \file htdocs/core/lib/geturl.lib.php
- * \brief This file contains functions dedicated to get URL.
+ * \brief This file contains functions dedicated to get URLs.
*/
/**
- * Function to get a content from an URL (use proxy if proxy defined)
+ * Function to get a content from an URL (use proxy if proxy defined).
+ * Support Dolibarr setup for timeout and proxy.
+ * Enhancement of CURL to add an anti SSRF protection.
*
* @param string $url URL to call.
* @param string $postorget 'POST', 'GET', 'HEAD', 'PUT', 'PUTALREADYFORMATED', 'POSTALREADYFORMATED', 'DELETE'
@@ -125,6 +127,7 @@ function getURLContent($url, $postorget = 'GET', $param = '', $followlocation =
$newUrl = $url;
$maxRedirection = 5;
$info = array();
+ $response = '';
do
{
@@ -132,8 +135,43 @@ function getURLContent($url, $postorget = 'GET', $param = '', $followlocation =
curl_setopt($ch, CURLOPT_URL, $newUrl);
- // TODO Test for $localurl on $newUrl
- // Parse $newUrl, check server address, then set parsed value with CURLOPT_CONNECT_TO
+ // Parse $newUrl
+ $newUrlArray = parse_url($newUrl);
+ $hosttocheck = $newUrlArray['host'];
+ $hosttocheck = str_replace(array('[', ']'), '', $hosttocheck); // Remove brackets of IPv6
+
+ if (in_array($hosttocheck, array('localhost', 'localhost.domain'))) {
+ $iptocheck = '127.0.0.1';
+ } else {
+ // TODO Resolve $iptocheck to get an IP and set CURLOPT_CONNECT_TO to use this ip
+ $iptocheck = $hosttocheck;
+ }
+
+ if (!filter_var($iptocheck, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6)) { // This is not an IP
+ $iptocheck = 0; //
+ }
+
+ if ($iptocheck) {
+ if ($localurl == 0) { // Only external url allowed (dangerous, may allow to get malware)
+ if (!filter_var($iptocheck, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
+ $info['http_code'] = 400;
+ $info['content'] = 'Error bad hostname IP (private or reserved range). Must be an external URL.';
+ break;
+ }
+ if (in_array($iptocheck, array('100.100.100.200'))) {
+ $info['http_code'] = 400;
+ $info['content'] = 'Error bad hostname IP (Used by Alibaba metadata). Must be an external URL.';
+ break;
+ }
+ }
+ if ($localurl == 1) { // Only local url allowed (dangerous, may allow to get metadata on server or make internal port scanning)
+ if (filter_var($iptocheck, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
+ $info['http_code'] = 400;
+ $info['content'] = 'Error bad hostname. Must be a local URL.';
+ break;
+ }
+ }
+ }
// Getting response from server
$response = curl_exec($ch);
diff --git a/htdocs/core/tpl/login.tpl.php b/htdocs/core/tpl/login.tpl.php
index 07229bcfed8..0eb3aa94c24 100644
--- a/htdocs/core/tpl/login.tpl.php
+++ b/htdocs/core/tpl/login.tpl.php
@@ -297,9 +297,9 @@ if (!empty($_SESSION['dol_loginmesg']))
if (!empty($conf->global->MAIN_EASTER_EGG_COMMITSTRIP)) {
include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
if (substr($langs->defaultlang, 0, 2) == 'fr') {
- $resgetcommitstrip = getURLContent("http://www.commitstrip.com/fr/feed/");
+ $resgetcommitstrip = getURLContent("https://www.commitstrip.com/fr/feed/");
} else {
- $resgetcommitstrip = getURLContent("http://www.commitstrip.com/en/feed/");
+ $resgetcommitstrip = getURLContent("https://www.commitstrip.com/en/feed/");
}
if ($resgetcommitstrip && $resgetcommitstrip['http_code'] == '200')
{
diff --git a/htdocs/multicurrency/class/multicurrency.class.php b/htdocs/multicurrency/class/multicurrency.class.php
index 2ce3277ae89..7a646386452 100644
--- a/htdocs/multicurrency/class/multicurrency.class.php
+++ b/htdocs/multicurrency/class/multicurrency.class.php
@@ -634,7 +634,7 @@ class MultiCurrency extends CommonObject
dol_syslog("Call url endpoint ".$urlendpoint);
- // TODO Use getURLContent() function instead.
+ // FIXME Use getURLContent() function instead.
$ch = curl_init($urlendpoint);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
diff --git a/test/phpunit/SecurityTest.php b/test/phpunit/SecurityTest.php
index b4bdf1d89ab..bc3595a8cfa 100644
--- a/test/phpunit/SecurityTest.php
+++ b/test/phpunit/SecurityTest.php
@@ -395,7 +395,7 @@ class SecurityTest extends PHPUnit\Framework\TestCase
global $conf;
include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
- $url = 'ftp://aaaa';
+ $url = 'ftp://mydomain.com';
$tmp = getURLContent($url);
print __METHOD__." url=".$url."\n";
$this->assertGreaterThan(0, strpos($tmp['curl_error_msg'], 'not supported')); // Test error if return does not contains 'not supported'
@@ -408,9 +408,34 @@ class SecurityTest extends PHPUnit\Framework\TestCase
$url = 'https://www.dolibarr.fr'; // This is a redirect 301 page
$tmp = getURLContent($url); // We DO follow
print __METHOD__." url=".$url."\n";
- //var_dump($tmp);
$this->assertEquals(200, $tmp['http_code'], 'GET url 301 with following -> 200'); // Test error if return does not contains 'not supported'
+ $url = 'http://localhost';
+ $tmp = getURLContent($url, 'GET', '', 0, array(), array('http', 'https'), 0); // Only external URL
+ print __METHOD__." url=".$url."\n";
+ $this->assertEquals(400, $tmp['http_code'], '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, $tmp['http_code'], 'GET url to '.$url.' that is a local URL'); // Test we receive an error because localtest.me 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, $tmp['http_code'], 'GET url to '.$url.' that is a local URL'); // Test we receive an error because localtest.me 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, $tmp['http_code'], 'GET url to '.$url.' that is a local URL'); // Test we receive an error because localtest.me 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, $tmp['http_code'], 'GET url to '.$url.' that resolves to a local URL'); // Test we receive an error because localtest.me is not an external URL
+ */
+
return 0;
}
}