From 102e0828fd162539afe7765bd0e8be906b84d365 Mon Sep 17 00:00:00 2001 From: Juan Leyva <juanleyvadelgado@gmail.com> Date: Thu, 21 Mar 2019 15:39:21 +0100 Subject: [PATCH] MDL-65075 tool_mobile: Allow auto-login keys only for requests from Moodle apps This change enhances security and avoid any kind of XSS attack. --- admin/tool/mobile/classes/api.php | 5 +++ admin/tool/mobile/classes/external.php | 2 ++ admin/tool/mobile/db/services.php | 3 +- admin/tool/mobile/lang/en/tool_mobile.php | 1 + admin/tool/mobile/tests/externallib_test.php | 34 ++++++++++++++++++++ admin/tool/mobile/upgrade.txt | 5 +++ 6 files changed, 49 insertions(+), 1 deletion(-) diff --git a/admin/tool/mobile/classes/api.php b/admin/tool/mobile/classes/api.php index bd49699e398..f2fc6806093 100644 --- a/admin/tool/mobile/classes/api.php +++ b/admin/tool/mobile/classes/api.php @@ -297,6 +297,11 @@ class api { throw new moodle_exception('enablewsdescription', 'webservice'); } + // Only requests from the Moodle mobile or desktop app. This enhances security to avoid any type of XSS attack. + if (!\core_useragent::is_moodle_app()) { + throw new moodle_exception('apprequired', 'tool_mobile'); + } + if (!is_https()) { throw new moodle_exception('httpsrequired', 'tool_mobile'); } diff --git a/admin/tool/mobile/classes/external.php b/admin/tool/mobile/classes/external.php index 17956350a89..830c0602390 100644 --- a/admin/tool/mobile/classes/external.php +++ b/admin/tool/mobile/classes/external.php @@ -262,6 +262,8 @@ class external extends external_api { /** * Creates an auto-login key for the current user. Is created only in https sites and is restricted by time and ip address. * + * Please note that it only works if the request comes from the Moodle mobile or desktop app. + * * @param string $privatetoken the user private token for validating the request * @return array with the settings and warnings * @since Moodle 3.2 diff --git a/admin/tool/mobile/db/services.php b/admin/tool/mobile/db/services.php index 0b50d6853cf..d53f7b41a7f 100644 --- a/admin/tool/mobile/db/services.php +++ b/admin/tool/mobile/db/services.php @@ -57,7 +57,8 @@ $functions = array( 'classname' => 'tool_mobile\external', 'methodname' => 'get_autologin_key', 'description' => 'Creates an auto-login key for the current user. - Is created only in https sites and is restricted by time and ip address.', + Is created only in https sites and is restricted by time, ip address and only works if the request + comes from the Moodle mobile or desktop app.', 'type' => 'write', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), diff --git a/admin/tool/mobile/lang/en/tool_mobile.php b/admin/tool/mobile/lang/en/tool_mobile.php index a3738785296..0b01cd02a57 100644 --- a/admin/tool/mobile/lang/en/tool_mobile.php +++ b/admin/tool/mobile/lang/en/tool_mobile.php @@ -27,6 +27,7 @@ $string['androidappid'] = 'Android app\'s unique identifier'; $string['androidappid_desc'] = 'This setting may be left as default unless you have a custom Android app.'; $string['apppolicy'] = 'App policy URL'; $string['apppolicy_help'] = 'The URL of a policy for app users which is listed on the About page in the app. If the field is left empty, the site policy URL will be used instead.'; +$string['apprequired'] = 'This functionality is only available when accessed via the Moodle mobile or desktop app.'; $string['autologinkeygenerationlockout'] = 'Auto-login key generation is blocked. You need to wait 6 minutes between requests.'; $string['autologinnotallowedtoadmins'] = 'Auto-login is not allowed for site admins.'; $string['cachedef_plugininfo'] = 'This stores the list of plugins with mobile addons'; diff --git a/admin/tool/mobile/tests/externallib_test.php b/admin/tool/mobile/tests/externallib_test.php index 4045ab14d26..1fee731019c 100644 --- a/admin/tool/mobile/tests/externallib_test.php +++ b/admin/tool/mobile/tests/externallib_test.php @@ -213,6 +213,10 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase { // Enable requeriments. $_GET['wstoken'] = $token->token; // Mock parameters. + // Fake the app. + core_useragent::instance(true, 'Mozilla/5.0 (Linux; Android 7.1.1; Moto G Play Build/NPIS26.48-43-2; wv) ' . + 'AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/71.0.3578.99 Mobile Safari/537.36 MoodleMobile'); + // Even if we force the password change for the current user we should be able to retrieve the key. set_user_preference('auth_forcepasswordchange', 1, $user->id); @@ -240,6 +244,10 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase { global $CFG; $this->resetAfterTest(true); + // Fake the app. + core_useragent::instance(true, 'Mozilla/5.0 (Linux; Android 7.1.1; Moto G Play Build/NPIS26.48-43-2; wv) ' . + 'AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/71.0.3578.99 Mobile Safari/537.36 MoodleMobile'); + // Need to disable webservices to verify that's checked. $CFG->enablewebservices = 0; $CFG->enablemobilewebservice = 0; @@ -256,6 +264,10 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase { public function test_get_autologin_key_missing_https() { global $CFG; + // Fake the app. + core_useragent::instance(true, 'Mozilla/5.0 (Linux; Android 7.1.1; Moto G Play Build/NPIS26.48-43-2; wv) ' . + 'AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/71.0.3578.99 Mobile Safari/537.36 MoodleMobile'); + // Need to simulate a non HTTPS site here. $CFG->wwwroot = str_replace('https:', 'http:', $CFG->wwwroot); @@ -276,6 +288,10 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase { $this->resetAfterTest(true); $this->setAdminUser(); + // Fake the app. + core_useragent::instance(true, 'Mozilla/5.0 (Linux; Android 7.1.1; Moto G Play Build/NPIS26.48-43-2; wv) ' . + 'AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/71.0.3578.99 Mobile Safari/537.36 MoodleMobile'); + $this->expectException('moodle_exception'); $this->expectExceptionMessage(get_string('autologinnotallowedtoadmins', 'tool_mobile')); $result = external::get_autologin_key(''); @@ -296,6 +312,10 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase { $token = external_generate_token_for_current_user($service); $_GET['wstoken'] = $token->token; // Mock parameters. + // Fake the app. + core_useragent::instance(true, 'Mozilla/5.0 (Linux; Android 7.1.1; Moto G Play Build/NPIS26.48-43-2; wv) ' . + 'AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/71.0.3578.99 Mobile Safari/537.36 MoodleMobile'); + $result = external::get_autologin_key($token->privatetoken); $result = external_api::clean_returnvalue(external::get_autologin_key_returns(), $result); @@ -311,6 +331,20 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase { $result = external::get_autologin_key($token->privatetoken); } + /** + * Test get_autologin_key missing app_request. + */ + public function test_get_autologin_key_missing_app_request() { + global $CFG; + + $this->resetAfterTest(true); + $this->setAdminUser(); + + $this->expectException('moodle_exception'); + $this->expectExceptionMessage(get_string('apprequired', 'tool_mobile')); + $result = external::get_autologin_key(''); + } + /** * Test get_content. */ diff --git a/admin/tool/mobile/upgrade.txt b/admin/tool/mobile/upgrade.txt index ec720c3491a..b368e939976 100644 --- a/admin/tool/mobile/upgrade.txt +++ b/admin/tool/mobile/upgrade.txt @@ -4,6 +4,11 @@ Information provided here is intended especially for developers. === 3.7 === * New external function tool_mobile::tool_mobile_call_external_function allows calling multiple external functions and returns all responses. + * External function tool_mobile::get_autologin_key now only works if the request comes from the Moodle mobile or desktop app. + This increases confidence that requests did originate from the mobile app, decreasing the likelihood of an XSS attack. + If you want to use this functionality, please override the Web Service via the override_webservice_execution callback although + this is not recommended or encouraged. +>>>>>>> a7ccde5003f... MDL-65075 tool_mobile: Allow auto-login keys only for requests from Moodle apps === 3.5 ===