From 5499336feff22f682448dd99cc00a9b36701fcd1 Mon Sep 17 00:00:00 2001
From: thomascube <thomas@roundcube.net>
Date: Tue, 21 Jul 2009 12:02:33 -0400
Subject: [PATCH] Use global request tokens and automatically protect all POST requests

---
 index.php                                |    9 +++-
 program/steps/addressbook/save.inc       |   10 ----
 program/include/rcmail.php               |   22 ++++------
 program/steps/settings/save_identity.inc |    8 ---
 program/include/rcube_template.php       |   27 +++++++++++--
 program/js/app.js                        |    2 
 program/steps/settings/save_prefs.inc    |    9 ----
 7 files changed, 42 insertions(+), 45 deletions(-)

diff --git a/index.php b/index.php
index 2767277..c5a1049 100644
--- a/index.php
+++ b/index.php
@@ -2,7 +2,7 @@
 /*
  +-------------------------------------------------------------------------+
  | RoundCube Webmail IMAP Client                                           |
- | Version 0.3-20090702                                                    |
+ | Version 0.3-20090721                                                    |
  |                                                                         |
  | Copyright (C) 2005-2009, RoundCube Dev. - Switzerland                   |
  |                                                                         |
@@ -143,11 +143,16 @@
 
 // check client X-header to verify request origin
 if ($OUTPUT->ajax_call) {
-  if (!$RCMAIL->config->get('devel_mode') && !rc_request_header('X-RoundCube-Referer')) {
+  if (!$RCMAIL->config->get('devel_mode') && rc_request_header('X-RoundCube-Request') != $RCMAIL->get_request_token()) {
     header('HTTP/1.1 404 Not Found');
     die("Invalid Request");
   }
 }
+// check request token in POST form submissions
+else if (!empty($_POST) && !$RCMAIL->check_request()) {
+  $OUTPUT->show_message('invalidrequest', 'error');
+  $OUTPUT->send($RCMAIL->task);
+}
 
 
 // not logged in -> show login page
diff --git a/program/include/rcmail.php b/program/include/rcmail.php
index a508e17..39edee4 100644
--- a/program/include/rcmail.php
+++ b/program/include/rcmail.php
@@ -872,33 +872,29 @@
   /**
    * Generate a unique token to be used in a form request
    *
-   * @param string Request identifier
    * @return string The request token
    */
-  public function get_request_token($key)
+  public function get_request_token()
   {
-    if (!$this->request_tokens[$key])
-      $_SESSION['request_tokens'][$key] = $this->request_tokens[$key] = md5(uniqid($key . rand(), true));
+    $key = $this->task;
     
-    return $this->request_tokens[$key];
+    if (!$_SESSION['request_tokens'][$key])
+      $_SESSION['request_tokens'][$key] = md5(uniqid($key . rand(), true));
+    
+    return $_SESSION['request_tokens'][$key];
   }
   
   
   /**
    * Check if the current request contains a valid token
    *
-   * @param string Request identifier
+   * @param int Request method
    * @return boolean True if request token is valid false if not
    */
-  public function check_request($key, $mode = RCUBE_INPUT_POST)
+  public function check_request($mode = RCUBE_INPUT_POST)
   {
     $token = get_input_value('_token', $mode);
-    $valid = !(empty($token) || $_SESSION['request_tokens'][$key] != $token);
-    
-    if ($valid)
-      unset($_SESSION['request_tokens'][$key]);
-    
-    return $valid;
+    return !empty($token) && $_SESSION['request_tokens'][$this->task] == $token;
   }
   
   
diff --git a/program/include/rcube_template.php b/program/include/rcube_template.php
index caf385a..0947944 100755
--- a/program/include/rcube_template.php
+++ b/program/include/rcube_template.php
@@ -59,6 +59,7 @@
         
         //$this->framed = $framed;
         $this->set_env('task', $task);
+        $this->set_env('request_token', $this->app->get_request_token());
 
         // load the correct skin (in case user-defined)
         $this->set_skin($this->config['skin']);
@@ -325,6 +326,9 @@
         $js = $this->framed ? "if(window.parent) {\n" : '';
         $js .= $this->get_js_commands() . ($this->framed ? ' }' : '');
         $this->add_script($js, 'head_top');
+        
+        // make sure all <form> tags have a valid request token
+        $template = preg_replace_callback('/<form\s+([^>]+)>/Ui', array($this, 'alter_form_tag'), $template);
 
         // call super method
         parent::write($template, $this->config['skin_path']);
@@ -514,7 +518,24 @@
      */
     private function check_condition($condition)
     {
-            return eval("return (".$this->parse_expression($condition).");");
+        return eval("return (".$this->parse_expression($condition).");");
+    }
+    
+    
+    /**
+     *
+     */
+    private function alter_form_tag($matches)
+    {
+        $out = $matches[0];
+        $attrib  = parse_attrib_string($matches[1]);
+      
+        if (strtolower($attrib['method']) == 'post') {
+            $hidden = new html_hiddenfield(array('name' => '_token', 'value' => $this->app->get_request_token()));
+            $out .= "\n" . $hidden->show();
+        }
+      
+        return $out;
     }
 
 
@@ -956,10 +977,6 @@
         if ($attrib['action']) {
             $hidden->add(array('name' => '_action', 'value' => $attrib['action']));
         }
-      
-        // generate request token
-        $request_key = $attrib['request'] ? $attrib['request'] : $attrib['action'];
-        $hidden->add(array('name' => '_token', 'value' => $this->app->get_request_token($request_key)));
       
         unset($attrib['task'], $attrib['request']);
         $attrib['action'] = './';
diff --git a/program/js/app.js b/program/js/app.js
index 332ee87..4ce3546 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -55,7 +55,7 @@
   // set jQuery ajax options
   jQuery.ajaxSetup({ cache:false,
     error:function(request, status, err){ ref.http_error(request, status, err); },
-    beforeSend:function(xmlhttp){ xmlhttp.setRequestHeader('X-RoundCube-Referer', bw.get_cookie('roundcube_sessid')); }
+    beforeSend:function(xmlhttp){ xmlhttp.setRequestHeader('X-RoundCube-Request', ref.env.request_token); }
   });
 
   // set environment variable(s)
diff --git a/program/steps/addressbook/save.inc b/program/steps/addressbook/save.inc
index 45cb638..639e0f2 100644
--- a/program/steps/addressbook/save.inc
+++ b/program/steps/addressbook/save.inc
@@ -5,7 +5,7 @@
  | program/steps/addressbook/save.inc                                    |
  |                                                                       |
  | This file is part of the RoundCube Webmail client                     |
- | Copyright (C) 2005-2007, RoundCube Dev. - Switzerland                 |
+ | Copyright (C) 2005-2009, RoundCube Dev. - Switzerland                 |
  | Licensed under the GNU GPL                                            |
  |                                                                       |
  | PURPOSE:                                                              |
@@ -21,14 +21,6 @@
 
 $cid = get_input_value('_cid', RCUBE_INPUT_POST);
 $return_action = empty($cid) ? 'add' : 'show';
-
-// check request token and exit if invalid
-if (!$RCMAIL->check_request('save.'.intval($cid), RCUBE_INPUT_POST))
-{
-  $OUTPUT->show_message('invalidrequest', 'error');
-  rcmail_overwrite_action($return_action);
-  return;
-}
 
 // cannot edit record
 if ($CONTACTS->readonly)
diff --git a/program/steps/settings/save_identity.inc b/program/steps/settings/save_identity.inc
index 86ff263..d36114c 100644
--- a/program/steps/settings/save_identity.inc
+++ b/program/steps/settings/save_identity.inc
@@ -5,7 +5,7 @@
  | program/steps/settings/save_identity.inc                              |
  |                                                                       |
  | This file is part of the RoundCube Webmail client                     |
- | Copyright (C) 2005-2007, RoundCube Dev. - Switzerland                 |
+ | Copyright (C) 2005-2009, RoundCube Dev. - Switzerland                 |
  | Licensed under the GNU GPL                                            |
  |                                                                       |
  | PURPOSE:                                                              |
@@ -26,12 +26,6 @@
 $a_boolean_cols = array('standard', 'html_signature');
 $updated = $default_id = false;
 
-// check request token
-if (!$RCMAIL->check_request('save-identity.'.intval(get_input_value('_iid', RCUBE_INPUT_POST)), RCUBE_INPUT_POST)) {
-  $OUTPUT->show_message('invalidrequest', 'error');
-  rcmail_overwrite_action('identities');
-  return;
-}
 // check input
 if (empty($_POST['_name']) || (empty($_POST['_email']) && IDENTITIES_LEVEL != 1 && IDENTITIES_LEVEL != 3))
   {
diff --git a/program/steps/settings/save_prefs.inc b/program/steps/settings/save_prefs.inc
index 7444a8b..8430ffd 100644
--- a/program/steps/settings/save_prefs.inc
+++ b/program/steps/settings/save_prefs.inc
@@ -5,7 +5,7 @@
  | program/steps/settings/save_prefs.inc                                 |
  |                                                                       |
  | This file is part of the RoundCube Webmail client                     |
- | Copyright (C) 2005-2007, RoundCube Dev. - Switzerland                 |
+ | Copyright (C) 2005-2009, RoundCube Dev. - Switzerland                 |
  | Licensed under the GNU GPL                                            |
  |                                                                       |
  | PURPOSE:                                                              |
@@ -18,13 +18,6 @@
  $Id$
 
 */
-
-// check request token and exit if invalid
-if (!$RCMAIL->check_request('save-prefs', RCUBE_INPUT_POST)) {
-  $OUTPUT->show_message('invalidrequest', 'error');
-  rcmail_overwrite_action('preferences');
-  return;
-}
 
 $a_user_prefs = array(
   'language'     => isset($_POST['_language']) ? get_input_value('_language', RCUBE_INPUT_POST) : $CONFIG['language'],

--
Gitblit v1.9.1