]> git.wh0rd.org Git - tt-rss.git/commitdiff
experimental CSRF protection
authorAndrew Dolgov <fox@madoka.volgo-balt.ru>
Mon, 26 Dec 2011 08:02:52 +0000 (12:02 +0400)
committerAndrew Dolgov <fox@madoka.volgo-balt.ru>
Mon, 26 Dec 2011 08:02:52 +0000 (12:02 +0400)
14 files changed:
backend.php
classes/article.php
classes/feeds.php
classes/handler.php
classes/pref_feeds.php
classes/pref_filters.php
classes/pref_instances.php
classes/pref_labels.php
classes/pref_prefs.php
classes/pref_users.php
classes/rpc.php
include/functions.php
js/functions.js
js/tt-rss.js

index 1805ce360b9538d85616d47b17213b6b1254059f..2e4da500fe794a11440acde7c9d35235f7726394 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-       set_include_path(get_include_path() . PATH_SEPARATOR . 
+       set_include_path(get_include_path() . PATH_SEPARATOR .
                dirname(__FILE__) . "/include");
 
        /* remove ill effects of magic quotes */
        $op = $_REQUEST["op"];
        @$method = $_REQUEST['subop'] ? $_REQUEST['subop'] : $_REQUEST["method"];
 
+       if (!$method)
+               $method = 'index';
+       else
+               $method = strtolower($method);
+
        /* Public calls compatibility shim */
 
        $public_calls = array("globalUpdateFeeds", "rss", "getUnread", "getProfiles", "share",
                return;
        }
 
+       $csrf_token = $_REQUEST['csrf_token'];
+
+       if (!$csrf_token)
+               error_log("[$op/$method] CSRF: [$csrf_token]\n", 3, "/tmp/csrf.log");
+
        require_once "functions.php";
        require_once "sessions.php";
        require_once "sanity_check.php";
                $handler = new $op($link, $_REQUEST);
 
                if ($handler) {
-                       if ($handler->before($method)) {
-                               if ($method && method_exists($handler, $method)) {
-                                       $handler->$method();
-                               } else if (method_exists($handler, 'index')) {
-                                       $handler->index();
+                       if (validate_csrf($csrf_token) || $handler->csrf_ignore($method)) {
+                               if ($handler->before($method)) {
+                                       if ($method && method_exists($handler, $method)) {
+                                               $handler->$method();
+                                       }
+                                       $handler->after();
+                                       return;
                                }
-                               $handler->after();
+                       } else {
+                               header("Content-Type: text/plain");
+                               print json_encode(array("error" => array("code" => 6)));
                                return;
                        }
                }
index 90ca129b9335c6fe458015ea44b3c1170413b3d7..30f0c7d10391b69e88bcd8f6b3a60d712f2cea6e 100644 (file)
@@ -1,6 +1,12 @@
 <?php\r
 class Article extends Protected_Handler {\r
 \r
+       function csrf_ignore($method) {\r
+               $csrf_ignored = array("redirect");\r
+\r
+               return array_search($method, $csrf_ignored) !== false;\r
+       }\r
+\r
        function redirect() {\r
                $id = db_escape_string($_REQUEST['id']);\r
 \r
index 3626e9fbc44ac0921e3155095c553d8a3f42a877..6b498ac00337820ea9be3284d0f23b3d04b52543 100644 (file)
@@ -1,6 +1,12 @@
 <?php\r
 class Feeds extends Protected_Handler {\r
 \r
+       function csrf_ignore($method) {\r
+               $csrf_ignored = array("index");\r
+\r
+               return array_search($method, $csrf_ignored) !== false;\r
+       }\r
+\r
        private function feedlist_init_cat($cat_id, $hidden = false) {\r
                $obj = array();\r
                $cat_id = (int) $cat_id;\r
index 53b52ea032c113fc6986e495cb206896d5746e79..404b8306b8e269d3c544aecd6f361b3e184d376f 100644 (file)
@@ -8,6 +8,10 @@ class Handler {
                $this->args = $args;
        }
 
+       function csrf_ignore($method) {
+               return true;
+       }
+
        function before() {
                return true;
        }
index 5df5eb939ebc0933ccc8f787d6421ffb86b59727..b83abd789ac15679fdb67bddf501d393e38630a0 100644 (file)
@@ -1,5 +1,12 @@
 <?php
 class Pref_Feeds extends Protected_Handler {
+
+       function csrf_ignore($method) {
+               $csrf_ignored = array("index", "getfeedtree", "add", "editcats", "editfeed");
+
+               return array_search($method, $csrf_ignored) !== false;
+       }
+
        function batch_edit_cbox($elem, $label = false) {
                print "<input type=\"checkbox\" title=\"".__("Check to enable field")."\"
                        onchange=\"dijit.byId('feedEditDlg').toggleField(this, '$elem', '$label')\">";
index d953a8d1d7197a69321c306fdc048675f193e50a..4ab12410f5cd98d91a7e16bb43c67437078337e2 100644 (file)
@@ -1,6 +1,12 @@
 <?php
 class Pref_Filters extends Protected_Handler {
 
+       function csrf_ignore($method) {
+               $csrf_ignored = array("index", "getfiltertree", "edit");
+
+               return array_search($method, $csrf_ignored) !== false;
+       }
+
        function filter_test($filter_type, $reg_exp,
                        $action_id, $action_param, $filter_param, $inverse, $feed_id) {
 
index 893d2b6bf7e036ee438e77a122c11884c8280a6f..aae5bbafb48bdbc9a7859925a73b023262f4bf76 100644 (file)
@@ -1,6 +1,12 @@
 <?php
 class Pref_Instances extends Protected_Handler {
 
+       function csrf_ignore($method) {
+               $csrf_ignored = array("index", "edit");
+
+               return array_search($method, $csrf_ignored) !== false;
+       }
+
        function before() {
                if (parent::before()) {
                        if ($_SESSION["access_level"] < 10) {
index 0d60731f3498b743370def6f174256010ddba24b..951ae45ed1492eb7610fb1fd0d06ed5e5c266ff7 100644 (file)
@@ -1,6 +1,12 @@
 <?php
 class Pref_Labels extends Protected_Handler {
 
+       function csrf_ignore($method) {
+               $csrf_ignored = array("index", "getlabeltree", "edit");
+
+               return array_search($method, $csrf_ignored) !== false;
+       }
+
        function edit() {
                $label_id = db_escape_string($_REQUEST['id']);
 
index 5a216d2b16867127b4eb0f5bae7c4ba3dbeecfee..03e39caa5c8a492a012c9f58d0bacaae656b7e0e 100644 (file)
@@ -1,6 +1,12 @@
 <?php
 class Pref_Prefs extends Protected_Handler {
 
+       function csrf_ignore($method) {
+               $csrf_ignored = array("index");
+
+               return array_search($method, $csrf_ignored) !== false;
+       }
+
        function changepassword() {
 
                $old_pw = $_POST["old_password"];
index b9d162fd295b93ccf51695cd476c07ea2552d30e..fe32ce14ca4611608558578e6a9299a1b50451e7 100644 (file)
@@ -1,6 +1,5 @@
 <?php
 class Pref_Users extends Protected_Handler {
-
                function before() {
                        if (parent::before()) {
                                if ($_SESSION["access_level"] < 10) {
@@ -12,6 +11,12 @@ class Pref_Users extends Protected_Handler {
                        return false;
                }
 
+               function csrf_ignore($method) {
+                       $csrf_ignored = array("index");
+
+                       return array_search($method, $csrf_ignored) !== false;
+               }
+
                function userdetails() {
 
                        header("Content-Type: text/xml");
index 8145b0407133adfeedb82c0977cde24ffefb8390..4cdaef93582a6467c8cf7b22cb31601242552bff 100644 (file)
@@ -1,6 +1,12 @@
 <?php
 class RPC extends Protected_Handler {
 
+       function csrf_ignore($method) {
+               $csrf_ignored = array("sanitycheck", "buttonplugin");
+
+               return array_search($method, $csrf_ignored) !== false;
+       }
+
        function setprofile() {
                $id = db_escape_string($_REQUEST["id"]);
 
index e561d8e3dcef3fd56fe7a8322cacb8c55eb5dfad..ed28fd25734bd8c6791485656a8c351197686446 100644 (file)
                                $_SESSION["uid"] = db_fetch_result($result, 0, "id");
                                $_SESSION["name"] = db_fetch_result($result, 0, "login");
                                $_SESSION["access_level"] = db_fetch_result($result, 0, "access_level");
+                               $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
 
                                db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
                                        $_SESSION["uid"]);
                }
        }
 
+       function validate_csrf($csrf_token) {
+               return $csrf_token == $_SESSION['csrf_token'];
+       }
+
        function validate_session($link) {
                if (SINGLE_USER_MODE) return true;
 
 
                $params["collapsed_feedlist"] = (int) get_pref($link, "_COLLAPSED_FEEDLIST");
 
+               $params["csrf_token"] = $_SESSION["csrf_token"];
+
                return $params;
        }
 
index 02134aafa115727bdacb00ab29e3c36c870cf14f..52201bd656af891c6220bb17f97899153ed35a5d 100644 (file)
@@ -1,6 +1,25 @@
 var notify_silent = false;
 var loading_progress = 0;
 var sanity_check_done = false;
+var init_params = {};
+
+Ajax.Base.prototype.initialize = Ajax.Base.prototype.initialize.wrap(
+       function (callOriginal, options) {
+
+               if (getInitParam("csrf_token") != undefined) {
+                       Object.extend(options, options || { });
+
+                       if (Object.isString(options.parameters))
+                               options.parameters = options.parameters.toQueryParams();
+                       else if (Object.isHash(options.parameters))
+                               options.parameters = options.parameters.toObject();
+
+                       options.parameters["csrf_token"] = getInitParam("csrf_token");
+               }
+
+               return callOriginal(options);
+       }
+);
 
 /* add method to remove element from array */
 
index 084a2186384a8398df7486543925a28c53ac4883..4f82545f9b607b4afca6aee057cf4df15e39314a 100644 (file)
@@ -5,7 +5,6 @@ var _active_feed_id = 0;
 var _active_feed_is_cat = false;
 var hotkey_prefix = false;
 var hotkey_prefix_pressed = false;
-var init_params = {};
 var _force_scheduled_update = false;
 var last_scheduled_update = false;