diff --git a/AUTHORS b/AUTHORS
index b701c768d3e..2404174e328 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -17,6 +17,7 @@ ownCloud is written by:
Sam Tuke
Simon Birnbach
Lukas Reschke
+ Christian Reiner
…
With help from many libraries and frameworks including:
diff --git a/core/ajax/requesttoken.php b/core/ajax/requesttoken.php
new file mode 100644
index 00000000000..96d5402e621
--- /dev/null
+++ b/core/ajax/requesttoken.php
@@ -0,0 +1,41 @@
+
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+* License as published by the Free Software Foundation; either
+* version 3 of the license, or any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+*
+* You should have received a copy of the GNU Affero General Public
+* License along with this library.
+* If not, see .
+*
+*/
+
+/**
+ * @file core/ajax/requesttoken.php
+ * @brief Ajax method to retrieve a fresh request protection token for ajax calls
+ * @return json: success/error state indicator including a fresh request token
+ * @author Christian Reiner
+ */
+require_once '../../lib/base.php';
+
+// don't load apps or filesystem for this task
+$RUNTIME_NOAPPS = TRUE;
+$RUNTIME_NOSETUPFS = TRUE;
+
+// Sanity checks
+// using OCP\JSON::callCheck() below protects the token refreshing itself.
+//OCP\JSON::callCheck ( );
+OCP\JSON::checkLoggedIn ( );
+// hand out a fresh token
+OCP\JSON::success ( array ( 'token' => OCP\Util::callRegister() ) );
+?>
diff --git a/core/js/eventsource.js b/core/js/eventsource.js
index e3ad7e3a671..45c63715a7e 100644
--- a/core/js/eventsource.js
+++ b/core/js/eventsource.js
@@ -40,7 +40,7 @@ OC.EventSource=function(src,data){
dataStr+=name+'='+encodeURIComponent(data[name])+'&';
}
}
- dataStr+='requesttoken='+OC.EventSource.requesttoken;
+ dataStr+='requesttoken='+OC.Request.Token;
if(!this.useFallBack && typeof EventSource !='undefined'){
this.source=new EventSource(src+'?'+dataStr);
this.source.onmessage=function(e){
diff --git a/core/js/requesttoken.js b/core/js/requesttoken.js
new file mode 100644
index 00000000000..0d78cd7e93b
--- /dev/null
+++ b/core/js/requesttoken.js
@@ -0,0 +1,55 @@
+/**
+ * ownCloud
+ *
+ * @file core/js/requesttoken.js
+ * @brief Routine to refresh the Request protection request token periodically
+ * @author Christian Reiner (arkascha)
+ * @copyright 2011-2012 Christian Reiner
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the license, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library.
+ * If not, see .
+ *
+ */
+
+OC.Request = {
+ // the request token
+ Token: {},
+ // the lifespan span (in secs)
+ Lifespan: {},
+ // method to refresh the local request token periodically
+ Refresh: function(){
+ // just a client side console log to preserve efficiency
+ console.log("refreshing request token (lifebeat)");
+ var dfd=new $.Deferred();
+ $.ajax({
+ type: 'POST',
+ url: OC.filePath('core','ajax','requesttoken.php'),
+ cache: false,
+ data: { },
+ dataType: 'json'
+ }).done(function(response){
+ // store refreshed token inside this class
+ OC.Request.Token=response.token;
+ dfd.resolve();
+ }).fail(dfd.reject);
+ return dfd;
+ }
+}
+// accept requesttoken and lifespan into the OC namespace
+OC.Request.Token = oc_requesttoken;
+OC.Request.Lifespan = oc_requestlifespan;
+// refresh the request token periodically shortly before it becomes invalid on the server side
+setInterval(OC.Request.Refresh,Math.floor(1000*OC.Request.Lifespan*0.93)), // 93% of lifespan value, close to when the token expires
+// early bind token as additional ajax argument for every single request
+$(document).bind('ajaxSend', function(elm, xhr, s){xhr.setRequestHeader('requesttoken', OC.Request.Token);});
diff --git a/core/templates/layout.user.php b/core/templates/layout.user.php
index 679be2657d4..b6d8a7604a2 100644
--- a/core/templates/layout.user.php
+++ b/core/templates/layout.user.php
@@ -11,6 +11,8 @@
var oc_webroot = '';
var oc_appswebroots = ;
var oc_current_user = '';
+ var oc_requesttoken = '';
+ var oc_requestlifespan = '';
@@ -24,13 +26,6 @@
echo '/>';
?>
-
diff --git a/lib/base.php b/lib/base.php
index f6afc8fe2fe..5a2decc6f63 100644
--- a/lib/base.php
+++ b/lib/base.php
@@ -240,6 +240,8 @@ class OC{
OC_Util::addScript( "jquery-tipsy" );
OC_Util::addScript( "oc-dialogs" );
OC_Util::addScript( "js" );
+ // request protection token MUST be defined after the jquery library but before any $('document').ready()
+ OC_Util::addScript( "requesttoken" );
OC_Util::addScript( "eventsource" );
OC_Util::addScript( "config" );
//OC_Util::addScript( "multiselect" );
diff --git a/lib/template.php b/lib/template.php
index 0987d6f0d88..681b3f0b140 100644
--- a/lib/template.php
+++ b/lib/template.php
@@ -157,6 +157,7 @@ class OC_Template{
$this->vars = array();
if($renderas == 'user') {
$this->vars['requesttoken'] = OC_Util::callRegister();
+ $this->vars['requestlifespan'] = OC_Util::$callLifespan;
}
$parts = explode('/', $app); // fix translation when app is something like core/lostpassword
$this->l10n = OC_L10N::get($parts[0]);
@@ -374,6 +375,7 @@ class OC_Template{
$page = new OC_TemplateLayout($this->renderas);
if($this->renderas == 'user') {
$page->assign('requesttoken', $this->vars['requesttoken']);
+ $page->assign('requestlifespan', $this->vars['requestlifespan']);
}
// Add custom headers
diff --git a/lib/util.php b/lib/util.php
index c89c4d8c7c1..777cb7a28fc 100755
--- a/lib/util.php
+++ b/lib/util.php
@@ -416,14 +416,29 @@ class OC_Util {
}
/**
- * @brief Register an get/post call. This is important to prevent CSRF attacks
- * Todo: Write howto
+ * @brief Static lifespan (in seconds) when a request token expires.
+ * @see OC_Util::callRegister()
+ * @see OC_Util::isCallRegistered()
+ * @description
+ * Also required for the client side to compute the piont in time when to
+ * request a fresh token. The client will do so when nearly 97% of the
+ * timespan coded here has expired.
+ */
+ public static $callLifespan = 3600; // 3600 secs = 1 hour
+
+ /**
+ * @brief Register an get/post call. Important to prevent CSRF attacks.
+ * @todo Write howto: CSRF protection guide
* @return $token Generated token.
+ * @description
+ * Creates a 'request token' (random) and stores it inside the session.
+ * Ever subsequent (ajax) request must use such a valid token to succeed,
+ * otherwise the request will be denied as a protection against CSRF.
+ * The tokens expire after a fixed lifespan.
+ * @see OC_Util::$callLifespan
+ * @see OC_Util::isCallRegistered()
*/
public static function callRegister() {
- //mamimum time before token exires
- $maxtime=(60*60); // 1 hour
-
// generate a random token.
$token=mt_rand(1000,9000).mt_rand(1000,9000).mt_rand(1000,9000);
@@ -436,7 +451,8 @@ class OC_Util {
foreach($_SESSION as $key=>$value) {
// search all tokens in the session
if(substr($key,0,12)=='requesttoken') {
- if($value+$maxtime