Thomas Bruederli
2016-01-16 699af1e5206ed9114322adaa3c25c1c969640a53
Protect download urls against CSRF using unique request tokens (#1490642)
Send X-Frame-Options headers with every HTTP response

Conflicts:
plugins/enigma/enigma.js
plugins/enigma/lib/enigma_ui.php
program/lib/Roundcube/rcube_message.php
12 files modified
68 ■■■■ changed files
plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php 2 ●●●●● patch | view | raw | blame | history
plugins/managesieve/managesieve.js 2 ●●● patch | view | raw | blame | history
plugins/zipdownload/zipdownload.js 2 ●●● patch | view | raw | blame | history
plugins/zipdownload/zipdownload.php 6 ●●●● patch | view | raw | blame | history
program/include/rcmail.php 2 ●●●●● patch | view | raw | blame | history
program/include/rcmail_output_html.php 6 ●●●● patch | view | raw | blame | history
program/js/app.js 24 ●●●●● patch | view | raw | blame | history
program/lib/Roundcube/rcube_message.php 9 ●●●●● patch | view | raw | blame | history
program/lib/Roundcube/rcube_output.php 5 ●●●●● patch | view | raw | blame | history
program/steps/addressbook/export.inc 2 ●●●●● patch | view | raw | blame | history
program/steps/mail/get.inc 4 ●●●● patch | view | raw | blame | history
program/steps/mail/viewsource.inc 4 ●●●● patch | view | raw | blame | history
plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php
@@ -397,6 +397,8 @@
                }
            }
            else if ($action == 'setget') {
                $this->rc->request_security_check(rcube_utils::INPUT_GET);
                $script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_GPC, true);
                $script      = $this->sieve->get_script($script_name);
plugins/managesieve/managesieve.js
@@ -181,7 +181,7 @@
  var id = this.filtersets_list.get_single_selection(),
    script = this.env.filtersets[id];
  location.href = this.env.comm_path+'&_action=plugin.managesieve-action&_act=setget&_set='+urlencode(script);
  this.goto_url('plugin.managesieve-action', {_act: 'setget', _set: script}, false, true);
};
// Set activate/deactivate request
plugins/zipdownload/zipdownload.js
@@ -54,7 +54,7 @@
    // default .eml download of single message
    if (mode == 'eml') {
        var uid = rcmail.get_single_uid();
        rcmail.goto_url('viewsource', rcmail.params_from_uid(uid, {_save: 1}));
        rcmail.goto_url('viewsource', rcmail.params_from_uid(uid, {_save: 1}), false, true);
        return;
    }
plugins/zipdownload/zipdownload.php
@@ -63,7 +63,7 @@
                '_action' => 'plugin.zipdownload.attachments',
                '_mbox'   => $rcmail->output->env['mailbox'],
                '_uid'    => $rcmail->output->env['uid'],
            ));
            ), false, false, true);
            $link = html::a(array('href' => $href, 'class' => 'button zipdownload'),
                rcube::Q($this->gettext('downloadall'))
@@ -120,6 +120,10 @@
    public function download_attachments()
    {
        $rcmail    = rcmail::get_instance();
        // require CSRF protected request
        $rcmail->request_security_check(rcube_utils::INPUT_GET);
        $imap      = $rcmail->get_storage();
        $temp_dir  = $rcmail->config->get('temp_dir');
        $tmpfname  = tempnam($temp_dir, 'zipdownload');
program/include/rcmail.php
@@ -813,6 +813,8 @@
            // this need to be full url to make redirects work
            $absolute = true;
        }
        else if ($secure && ($token = $this->get_request_token()))
            $url .= $delm . '_token=' . urlencode($token);
        if ($absolute || $full) {
            // add base path to this Roundcube installation
program/include/rcmail_output_html.php
@@ -514,10 +514,10 @@
        // write all javascript commands
        $this->add_script($commands, 'head_top');
        // send clickjacking protection headers
        // allow (legal) iframe content to be loaded
        $iframe = $this->framed || $this->env['framed'];
        if (!headers_sent() && ($xframe = $this->app->config->get('x_frame_options', 'sameorigin'))) {
            header('X-Frame-Options: ' . ($iframe && $xframe == 'deny' ? 'sameorigin' : $xframe));
        if (!headers_sent() && $iframe && $this->app->config->get('x_frame_options', 'sameorigin') === 'deny') {
            header('X-Frame-Options: sameorigin', true);
        }
        // call super method
program/js/app.js
@@ -999,7 +999,7 @@
            break;
        }
        this.goto_url('get', qstring+'&_download=1', false);
        this.goto_url('get', qstring+'&_download=1', false, true);
        break;
      case 'select-all':
@@ -1205,10 +1205,10 @@
      case 'download':
        if (this.env.action == 'get') {
          location.href = location.href.replace(/_frame=/, '_download=');
          location.href = this.secure_url(location.href.replace(/_frame=/, '_download='));
        }
        else if (uid = this.get_single_uid()) {
          this.goto_url('viewsource', this.params_from_uid(uid, {_save: 1}));
          this.goto_url('viewsource', this.params_from_uid(uid, {_save: 1}), false, true);
        }
        break;
@@ -1296,13 +1296,13 @@
      case 'export':
        if (this.contact_list.rowcount > 0) {
          this.goto_url('export', { _source: this.env.source, _gid: this.env.group, _search: this.env.search_request });
          this.goto_url('export', { _source: this.env.source, _gid: this.env.group, _search: this.env.search_request }, false, true);
        }
        break;
      case 'export-selected':
        if (this.contact_list.rowcount > 0) {
          this.goto_url('export', { _source: this.env.source, _gid: this.env.group, _cid: this.contact_list.get_selection().join(',') });
          this.goto_url('export', { _source: this.env.source, _gid: this.env.group, _cid: this.contact_list.get_selection().join(',') }, false, true);
        }
        break;
@@ -1417,7 +1417,7 @@
    if (task == 'mail')
      url += '&_mbox=INBOX';
    else if (task == 'logout' && !this.env.server_error) {
      url += '&_token=' + this.env.request_token;
      url = this.secure_url(url);
      this.clear_compose_data();
    }
@@ -1465,6 +1465,12 @@
    return url + '?' + name + '=' + value;
  };
  // append CSRF protection token to the given url
  this.secure_url = function(url)
  {
    return this.add_url(url, '_token', this.env.request_token);
  },
  this.is_framed = function()
  {
@@ -7282,9 +7288,11 @@
    }
  };
  this.goto_url = function(action, query, lock)
  this.goto_url = function(action, query, lock, secure)
  {
    this.redirect(this.url(action, query), lock);
    var url = this.url(action, query)
    if (secure) url = this.secure_url(url);
    this.redirect(url, lock);
  };
  this.location_href = function(url, target, frame)
program/lib/Roundcube/rcube_message.php
@@ -105,10 +105,11 @@
        $this->opt = array(
            'safe' => $this->is_safe,
            'prefer_html' => $this->app->config->get('prefer_html'),
            'get_url' => $this->app->url(array(
                'action' => 'get',
                'mbox'   => $this->storage->get_folder(),
                'uid'    => $uid))
            'get_url'     => $this->app->url(array(
                    'action' => 'get',
                    'mbox'   => $this->storage->get_folder(),
                    'uid'    => $uid),
                false, false, true)
        );
        if (!empty($this->headers->structure)) {
program/lib/Roundcube/rcube_output.php
@@ -190,6 +190,11 @@
        // Request browser to disable DNS prefetching (CVE-2010-0464)
        header("X-DNS-Prefetch-Control: off");
        // send CSRF and clickjacking protection headers
        if ($xframe = $this->app->config->get('x_frame_options', 'sameorigin')) {
            header('X-Frame-Options: ' . $xframe);
        }
    }
    /**
program/steps/addressbook/export.inc
@@ -21,6 +21,8 @@
 +-----------------------------------------------------------------------+
*/
$RCMAIL->request_security_check(rcube_utils::INPUT_GET);
// Use search result
if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search']])) {
    $sort_col = $RCMAIL->config->get('addressbook_sort_col', 'name');
program/steps/mail/get.inc
@@ -131,6 +131,10 @@
            exit;
        }
        // require CSRF protected url for downloads
        if ($plugin['download'])
            $RCMAIL->request_security_check(rcube_utils::INPUT_GET);
        // overwrite modified vars from plugin
        $mimetype   = $plugin['mimetype'];
        $extensions = rcube_mime::get_mime_extensions($mimetype);
program/steps/mail/viewsource.inc
@@ -19,6 +19,10 @@
 +-----------------------------------------------------------------------+
*/
if (!empty($_GET['_save'])) {
    $RCMAIL->request_security_check(rcube_utils::INPUT_GET);
}
ob_end_clean();
// similar code as in program/steps/mail/get.inc