From 5124deed089ecd000b3c0e9989259d8a790d56ad Mon Sep 17 00:00:00 2001 From: Adrian Greeve Date: Mon, 8 Jan 2018 08:36:47 +0800 Subject: [PATCH] MDL-61131 repositories: Added a key to verify incoming urls. --- lang/en/repository.php | 2 ++ repository/filepicker.js | 6 +++++- repository/lib.php | 19 +++++++++++++++++++ repository/repository_ajax.php | 11 +++++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/lang/en/repository.php b/lang/en/repository.php index 697e60f9ee0..7b362b9089e 100644 --- a/lang/en/repository.php +++ b/lang/en/repository.php @@ -164,6 +164,7 @@ $string['manage'] = 'Manage repositories'; $string['manageinstances'] = 'Manage instances'; $string['manageurl'] = 'Manage'; $string['manageuserrepository'] = 'Manage individual repository'; +$string['missingsourcekey'] = 'The source key is missing. This key must also be provided to retrieve the file.'; $string['moving'] = 'Moving'; $string['name'] = 'Name'; $string['newfolder'] = 'New folder'; @@ -220,6 +221,7 @@ $string['setmainfile'] = 'Set main file'; $string['setmainfile_help'] = 'If there are multiple files in the folder, the main file is the one that appears on the view page. Other files such as images or videos may be embedded in it. In filemanager the main file is indicated with a title in bold.'; $string['siteinstances'] = 'Repositories instances of the site'; $string['size'] = 'Size'; +$string['sourcekeymismatch'] = 'The source url does not match the sourcekey.'; $string['submit'] = 'Submit'; $string['sync'] = 'Sync'; $string['syncfiletimeout'] = 'Sync file timeout'; diff --git a/repository/filepicker.js b/repository/filepicker.js index 31067b7c749..169b1fc8ad8 100644 --- a/repository/filepicker.js +++ b/repository/filepicker.js @@ -1127,6 +1127,7 @@ M.core_filepicker.init = function(Y, options) { selectnode.one('.fp-setauthor input').set('value', args.author ? args.author : this.options.author); this.set_selected_license(selectnode.one('.fp-setlicense'), args.license); selectnode.one('form #filesource-'+client_id).set('value', args.source); + selectnode.one('form #filesourcekey-'+client_id).set('value', args.sourcekey); // display static information about a file (when known) var attrs = ['datemodified','datecreated','size','license','author','dimensions']; @@ -1170,7 +1171,8 @@ M.core_filepicker.init = function(Y, options) { var repository_id = this.active_repo.id; var title = selectnode.one('.fp-saveas input').get('value'); var filesource = selectnode.one('form #filesource-'+client_id).get('value'); - var params = {'title':title, 'source':filesource, 'savepath': this.options.savepath}; + var filesourcekey = selectnode.one('form #filesourcekey-'+client_id).get('value'); + var params = {'title':title, 'source':filesource, 'savepath': this.options.savepath, sourcekey: filesourcekey}; var license = selectnode.one('.fp-setlicense select'); if (license) { params['license'] = license.get('value'); @@ -1228,6 +1230,8 @@ M.core_filepicker.init = function(Y, options) { var elform = selectnode.one('form'); elform.appendChild(Y.Node.create(''). setAttrs({type:'hidden',id:'filesource-'+client_id})); + elform.appendChild(Y.Node.create(''). + setAttrs({type:'hidden',id:'filesourcekey-'+client_id})); elform.on('keydown', function(e) { if (e.keyCode == 13) { getfile.simulate('click'); diff --git a/repository/lib.php b/repository/lib.php index 5d0d905be5b..188193dcf46 100644 --- a/repository/lib.php +++ b/repository/lib.php @@ -2194,6 +2194,11 @@ abstract class repository implements cacheable_object { $file =& $list[$i]; $converttoobject = false; } + + if (isset($file['source'])) { + $file['sourcekey'] = sha1($file['source'] . self::get_secret_key() . sesskey()); + } + if (isset($file['size'])) { $file['size'] = (int)$file['size']; $file['size_f'] = display_size($file['size']); @@ -2789,6 +2794,20 @@ abstract class repository implements cacheable_object { debugging('The method repository::uses_post_requests() is deprecated and must not be used anymore.', DEBUG_DEVELOPER); return false; } + + /** + * Generate a secret key to be used for passing sensitive information around. + * + * @return string repository secret key. + */ + final static public function get_secret_key() { + global $CFG; + + if (!isset($CFG->reposecretkey)) { + set_config('reposecretkey', time() . random_string(32)); + } + return $CFG->reposecretkey; + } } /** diff --git a/repository/repository_ajax.php b/repository/repository_ajax.php index 51ecb3b21c5..7c8affc0e94 100644 --- a/repository/repository_ajax.php +++ b/repository/repository_ajax.php @@ -40,6 +40,7 @@ $env = optional_param('env', 'filepicker', PARAM_ALPHA); // Opened in edi $license = optional_param('license', $CFG->sitedefaultlicense, PARAM_TEXT); $author = optional_param('author', '', PARAM_TEXT); // File author $source = optional_param('source', '', PARAM_RAW); // File to download +$sourcekey = optional_param('sourcekey', '', PARAM_RAW); // Used to verify the source. $itemid = optional_param('itemid', 0, PARAM_INT); // Itemid $page = optional_param('page', '', PARAM_RAW); // Page $maxbytes = optional_param('maxbytes', 0, PARAM_INT); // Maxbytes @@ -157,6 +158,16 @@ switch ($action) { // allow external links in url element all the time $allowexternallink = ($allowexternallink || ($env == 'url')); + // Validate the sourcekey. + if (empty($sourcekey)) { + throw new moodle_exception('missingsourcekey', 'repository'); + } + + // Check that the sourcekey matches. + if (sha1($source . repository::get_secret_key() . sesskey()) !== $sourcekey) { + throw new moodle_exception('sourcekeymismatch', 'repository'); + } + $reference = $repo->get_file_reference($source); // Use link of the files