Merge branch 'MDL-69050-master-allowlist' of git://github.com/mudrd8mz/moodle into master

This commit is contained in:
Sara Arjona
2020-10-01 14:51:11 +02:00
36 changed files with 381 additions and 271 deletions
+1 -1
View File
@@ -733,7 +733,7 @@ $cache = '.var_export($cache, true).';
/**
* List all core subsystems and their location
*
* This is a whitelist of components that are part of the core and their
* This is a list of components that are part of the core and their
* language strings are defined in /lang/en/<<subsystem>>.php. If a given
* plugin is not listed here and it does not have proper plugintype prefix,
* then it is considered as course activity module.
+31 -31
View File
@@ -15,7 +15,7 @@
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Contains a class providing functions used to check the host/port black/whitelists for curl.
* Contains a class providing functions used to check the allowed/blocked host/ports for curl.
*
* @package core
* @copyright 2016 Jake Dallimore
@@ -32,7 +32,7 @@ defined('MOODLE_INTERNAL') || exit();
* Host and port checking for curl.
*
* This class provides a means to check URL/host/port against the system-level cURL security entries.
* It does not provide a means to add URLs, hosts or ports to the black/white lists; this is configured manually
* It does not provide a means to add URLs, hosts or ports to the allowed/blocked lists; this is configured manually
* via the site admin section of Moodle (See: 'Site admin' > 'Security' > 'HTTP Security').
*
* This class is currently used by the 'curl' wrapper class in lib/filelib.php.
@@ -55,12 +55,12 @@ class curl_security_helper extends curl_security_helper_base {
];
/**
* Checks whether the given URL is blacklisted by checking its address and port number against the black/white lists.
* Checks whether the given URL is blocked by checking its address and port number against the allow/block lists.
* The behaviour of this function can be classified as strict, as it returns true for URLs which are invalid or
* could not be parsed, as well as those valid URLs which were found in the blacklist.
* could not be parsed, as well as those valid URLs which were found in the blocklist.
*
* @param string $urlstring the URL to check.
* @return bool true if the URL is blacklisted or invalid and false if the URL is not blacklisted.
* @return bool true if the URL is blocked or invalid and false if the URL is not blocked.
*/
public function url_is_blocked($urlstring) {
// If no config data is present, then all hosts/ports are allowed.
@@ -85,7 +85,7 @@ class curl_security_helper extends curl_security_helper_base {
}
if ($parsed['port'] && $parsed['host']) {
// Check the host and port against the blacklist/whitelist entries.
// Check the host and port against the allow/block entries.
return $this->host_is_blocked($parsed['host']) || $this->port_is_blocked($parsed['port']);
}
return true;
@@ -114,9 +114,9 @@ class curl_security_helper extends curl_security_helper_base {
* - This will perform a DNS reverse lookup if required.
*
* The behaviour of this function can be classified as strict, as it returns true for hosts which are invalid or
* could not be parsed, as well as those valid URLs which were found in the blacklist.
* could not be parsed, as well as those valid URLs which were found in the blocklist.
*
* @param string $host the host component of the URL to check against the blacklist.
* @param string $host the host component of the URL to check against the blocklist.
* @return bool true if the host is both valid and blocked, false otherwise.
*/
protected function host_is_blocked($host) {
@@ -126,7 +126,7 @@ class curl_security_helper extends curl_security_helper_base {
// Fix for square brackets in the 'host' portion of the URL (only occurs if an IPv6 address is specified).
$host = str_replace(array('[', ']'), '', $host); // RFC3986, section 3.2.2.
$blacklistedhosts = $this->get_blacklisted_hosts_by_category();
$blockedhosts = $this->get_blocked_hosts_by_category();
if (ip_utils::is_ip_address($host)) {
if ($this->address_explicitly_blocked($host)) {
@@ -134,7 +134,7 @@ class curl_security_helper extends curl_security_helper_base {
}
// Only perform a reverse lookup if there is a point to it (i.e. we have rules to check against).
if ($blacklistedhosts['domain'] || $blacklistedhosts['domainwildcard']) {
if ($blockedhosts['domain'] || $blockedhosts['domainwildcard']) {
// DNS reverse lookup - supports both IPv4 and IPv6 address formats.
$hostname = gethostbyaddr($host);
if ($hostname !== $host && $this->host_explicitly_blocked($hostname)) {
@@ -147,7 +147,7 @@ class curl_security_helper extends curl_security_helper_base {
}
// Only perform a forward lookup if there are IP rules to check against.
if ($blacklistedhosts['ipv4'] || $blacklistedhosts['ipv6']) {
if ($blockedhosts['ipv4'] || $blockedhosts['ipv6']) {
// DNS forward lookup - returns a list of only IPv4 addresses!
$hostips = $this->get_host_list_by_name($host);
@@ -156,7 +156,7 @@ class curl_security_helper extends curl_security_helper_base {
return true;
}
// If any of the returned IPs are in the blacklist, block the request.
// If any of the returned IPs are in the blocklist, block the request.
foreach ($hostips as $hostip) {
if ($this->address_explicitly_blocked($hostip)) {
return true;
@@ -182,10 +182,10 @@ class curl_security_helper extends curl_security_helper_base {
}
/**
* Checks whether the given port is blocked, as determined by its absence on the ports whitelist.
* Ports are assumed to be blocked unless found in the whitelist.
* Checks whether the given port is blocked, as determined by its absence on the ports allowlist.
* Ports are assumed to be blocked unless found in the allowlist.
*
* @param integer|string $port the port to check against the ports whitelist.
* @param integer|string $port the port to check against the ports allowlist.
* @return bool true if the port is blocked, false otherwise.
*/
protected function port_is_blocked($port) {
@@ -194,28 +194,28 @@ class curl_security_helper extends curl_security_helper_base {
if (empty($port) || (string)$portnum !== (string)$port || $port < 0) {
return true;
}
$allowedports = $this->get_whitelisted_ports();
$allowedports = $this->get_allowed_ports();
return !empty($allowedports) && !in_array($portnum, $allowedports);
}
/**
* Convenience method to check whether we have any entries in the host blacklist or ports whitelist admin settings.
* If no entries are found at all, the assumption is that the blacklist is disabled entirely.
* Convenience method to check whether we have any entries in the host blocklist or ports allowlist admin settings.
* If no entries are found at all, the assumption is that the blocklist is disabled entirely.
*
* @return bool true if one or more entries exist, false otherwise.
*/
public function is_enabled() {
return (!empty($this->get_whitelisted_ports()) || !empty($this->get_blacklisted_hosts()));
return (!empty($this->get_allowed_ports()) || !empty($this->get_blocked_hosts()));
}
/**
* Checks whether the input address is blocked by at any of the IPv4 or IPv6 address rules.
*
* @param string $addr the ip address to check.
* @return bool true if the address is covered by an entry in the blacklist, false otherwise.
* @return bool true if the address is covered by an entry in the blocklist, false otherwise.
*/
protected function address_explicitly_blocked($addr) {
$blockedhosts = $this->get_blacklisted_hosts_by_category();
$blockedhosts = $this->get_blocked_hosts_by_category();
$iphostsblocked = array_merge($blockedhosts['ipv4'], $blockedhosts['ipv6']);
return address_in_subnet($addr, implode(',', $iphostsblocked));
}
@@ -224,10 +224,10 @@ class curl_security_helper extends curl_security_helper_base {
* Checks whether the input hostname is blocked by any of the domain/wildcard rules.
*
* @param string $host the hostname to check
* @return bool true if the host is covered by an entry in the blacklist, false otherwise.
* @return bool true if the host is covered by an entry in the blocklist, false otherwise.
*/
protected function host_explicitly_blocked($host) {
$blockedhosts = $this->get_blacklisted_hosts_by_category();
$blockedhosts = $this->get_blocked_hosts_by_category();
$domainhostsblocked = array_merge($blockedhosts['domain'], $blockedhosts['domainwildcard']);
return ip_utils::is_domain_in_allowed_list($host, $domainhostsblocked);
}
@@ -238,10 +238,10 @@ class curl_security_helper extends curl_security_helper_base {
*
* @return array of host/domain/ip entries from the 'curlsecurityblockedhosts' config.
*/
protected function get_blacklisted_hosts_by_category() {
protected function get_blocked_hosts_by_category() {
// For each of the admin setting entries, check and place in the correct section of the config array.
$config = ['ipv6' => [], 'ipv4' => [], 'domain' => [], 'domainwildcard' => []];
$entries = $this->get_blacklisted_hosts();
$entries = $this->get_blocked_hosts();
foreach ($entries as $entry) {
if (ip_utils::is_ipv6_address($entry) || ip_utils::is_ipv6_range($entry)) {
$config['ipv6'][] = $entry;
@@ -257,11 +257,11 @@ class curl_security_helper extends curl_security_helper_base {
}
/**
* Helper that returns the whitelisted ports, as defined in the 'curlsecurityallowedport' setting.
* Helper that returns the allowed ports, as defined in the 'curlsecurityallowedport' setting.
*
* @return array the array of whitelisted ports.
* @return array the array of allowed ports.
*/
protected function get_whitelisted_ports() {
protected function get_allowed_ports() {
global $CFG;
if (!isset($CFG->curlsecurityallowedport)) {
return [];
@@ -272,11 +272,11 @@ class curl_security_helper extends curl_security_helper_base {
}
/**
* Helper that returns the blacklisted hosts, as defined in the 'curlsecurityblockedhosts' setting.
* Helper that returns the blocked hosts, as defined in the 'curlsecurityblockedhosts' setting.
*
* @return array the array of blacklisted host entries.
* @return array the array of blocked host entries.
*/
protected function get_blacklisted_hosts() {
protected function get_blocked_hosts() {
global $CFG;
if (!isset($CFG->curlsecurityblockedhosts)) {
return [];
+10 -4
View File
@@ -38,7 +38,7 @@ class mustache_engine extends \Mustache_Engine {
/**
* @var string[] Names of helpers that aren't allowed to be called within other helpers.
*/
private $blacklistednestedhelpers = [];
private $disallowednestedhelpers = [];
/**
* Mustache engine constructor.
@@ -47,13 +47,19 @@ class mustache_engine extends \Mustache_Engine {
* $options = [
* // A list of helpers (by name) to prevent from executing within the rendering
* // of other helpers.
* 'blacklistednestedhelpers' => ['js']
* 'disallowednestedhelpers' => ['js']
* ];
* @param array $options [description]
*/
public function __construct(array $options = []) {
if (isset($options['blacklistednestedhelpers'])) {
$this->blacklistednestedhelpers = $options['blacklistednestedhelpers'];
debugging('blacklistednestedhelpers option is deprecated. Use disallowednestedhelpers instead.', DEBUG_DEVELOPER);
$this->disallowednestedhelpers = $options['blacklistednestedhelpers'];
}
if (isset($options['disallowednestedhelpers'])) {
$this->disallowednestedhelpers = $options['disallowednestedhelpers'];
}
parent::__construct($options);
@@ -69,7 +75,7 @@ class mustache_engine extends \Mustache_Engine {
public function getHelpers()
{
if (!isset($this->helpers)) {
$this->helpers = new mustache_helper_collection(null, $this->blacklistednestedhelpers);
$this->helpers = new mustache_helper_collection(null, $this->disallowednestedhelpers);
}
return $this->helpers;
@@ -34,7 +34,7 @@ class mustache_helper_collection extends \Mustache_HelperCollection {
/**
* @var string[] Names of helpers that aren't allowed to be called within other helpers.
*/
private $blacklistednestedhelpers = [];
private $disallowednestedhelpers = [];
/**
* Helper Collection constructor.
@@ -44,43 +44,43 @@ class mustache_helper_collection extends \Mustache_HelperCollection {
* @throws \Mustache_Exception_InvalidArgumentException if the $helpers argument isn't an array or Traversable
*
* @param array|\Traversable $helpers (default: null)
* @param string[] $blacklistednestedhelpers Names of helpers that aren't allowed to be called within other helpers.
* @param string[] $disallowednestedhelpers Names of helpers that aren't allowed to be called within other helpers.
*/
public function __construct($helpers = null, array $blacklistednestedhelpers = []) {
$this->blacklistednestedhelpers = $blacklistednestedhelpers;
public function __construct($helpers = null, array $disallowednestedhelpers = []) {
$this->disallowednestedhelpers = $disallowednestedhelpers;
parent::__construct($helpers);
}
/**
* Add a helper to this collection.
*
* This function has overridden the parent implementation to provide blacklist
* This function has overridden the parent implementation to provide disallowing
* functionality for certain helpers to prevent them being called from within
* other helpers. This is because the JavaScript helper can be used in a
* security exploit if it can be nested.
*
* The function will wrap callable helpers in an anonymous function that strips
* out the blacklisted helpers from the source string before giving it to the
* helper function. This prevents the blacklisted helper functions from being
* out the disallowed helpers from the source string before giving it to the
* helper function. This prevents the disallowed helper functions from being
* called by nested render functions from within other helpers.
*
* @see \Mustache_HelperCollection::add()
* @param string $name
* @param mixed $helper
*/
public function add($name, $helper)
{
$blacklist = $this->blacklistednestedhelpers;
public function add($name, $helper) {
if (is_callable($helper) && !empty($blacklist)) {
$helper = function($source, \Mustache_LambdaHelper $lambdahelper) use ($helper, $blacklist) {
$disallowedlist = $this->disallowednestedhelpers;
// Temporarily override the blacklisted helpers to return nothing
if (is_callable($helper) && !empty($disallowedlist)) {
$helper = function($source, \Mustache_LambdaHelper $lambdahelper) use ($helper, $disallowedlist) {
// Temporarily override the disallowed helpers to return nothing
// so that they can't be executed from within other helpers.
$disabledhelpers = $this->disable_helpers($blacklist);
$disabledhelpers = $this->disable_helpers($disallowedlist);
// Call the original function with the modified sources.
$result = call_user_func($helper, $source, $lambdahelper);
// Restore the original blacklisted helper implementations now
// Restore the original disallowed helper implementations now
// that this helper has finished executing so that the rest of
// the rendering process continues to work correctly.
$this->restore_helpers($disabledhelpers);
@@ -89,7 +89,7 @@ class mustache_helper_collection extends \Mustache_HelperCollection {
// This is done because a secondary render is called on the result
// of a helper function if it still includes mustache tags. See
// the section function of Mustache_Compiler for details.
return $this->strip_blacklisted_helpers($blacklist, $result);
return $this->strip_disallowed_helpers($disallowedlist, $result);
};
}
@@ -137,18 +137,18 @@ class mustache_helper_collection extends \Mustache_HelperCollection {
}
/**
* Parse the given string and remove any reference to blacklisted helpers.
* Parse the given string and remove any reference to disallowed helpers.
*
* E.g.
* $blacklist = ['js'];
* $disallowedlist = ['js'];
* $string = "core, move, {{#js}} some nasty JS hack {{/js}}"
* result: "core, move, {{}}"
*
* @param string[] $blacklist List of helper names to strip
* @param string[] $disallowedlist List of helper names to strip
* @param string $string String to parse
* @return string Parsed string
*/
public function strip_blacklisted_helpers($blacklist, $string) {
public function strip_disallowed_helpers($disallowedlist, $string) {
$starttoken = \Mustache_Tokenizer::T_SECTION;
$endtoken = \Mustache_Tokenizer::T_END_SECTION;
if ($endtoken == '/') {
@@ -160,7 +160,7 @@ class mustache_helper_collection extends \Mustache_HelperCollection {
// the user is able to change the delimeters on a per template
// basis so they may not be curly braces.
return '/\s*' . $starttoken . '\s*'. $name . '\W+.*' . $endtoken . '\s*' . $name . '\s*/';
}, $blacklist);
}, $disallowedlist);
// This will strip out unwanted helpers from the $source string
// before providing it to the original helper function.
@@ -168,9 +168,25 @@ class mustache_helper_collection extends \Mustache_HelperCollection {
// Before:
// "core, move, {{#js}} some nasty JS hack {{/js}}"
// After:
// "core, move, {{}}"
// "core, move, {{}}".
return preg_replace_callback($regexes, function() {
return '';
}, $string);
}
/**
* Parse the given string and remove any reference to disallowed helpers.
*
* @deprecated Deprecated since Moodle 3.10 (MDL-69050) - use {@see self::strip_disallowed_helpers()}
* @param string[] $disallowedlist List of helper names to strip
* @param string $string String to parse
* @return string Parsed string
*/
public function strip_blacklisted_helpers($disallowedlist, $string) {
debugging('mustache_helper_collection::strip_blacklisted_helpers() is deprecated. ' .
'Please use mustache_helper_collection::strip_disallowed_helpers() instead.', DEBUG_DEVELOPER);
return $this->strip_disallowed_helpers($disallowedlist, $string);
}
}