Hi everybody.
My web application is based on asynchronous requests. Timer widget is working and updating it's status every second by AJAX (yes, it is necessary).
I am sending with each AJAX my CSRF tokens:
project_data.append(csrf_name_key,csrf_name_value);
project_data.append(csrf_value_key,csrf_value_value);
And in response I am updating that global variables:
function setCSRF(response) {
csrf_name_key = response.nameKey;
csrf_name_value = response.name;
csrf_value_key = response.valueKey;
csrf_value_value = response.value;
}
Everything is generally fine. But if I will do another AJAX for example when I change task in todo list to "done" it sometimes ending with error because I am sending AJAX before I am getting new tokens from previous request.
I really don't know how to do solve that problem. First idea was that I will make "like stack array" with 5 different tokens but one https request = one pair of tokens and I can't generate it.
Maybe some type of queue of ajax requests, but what with doing them in a right time - I don't know.
My actual pseudo-solution is "if failed try again max 10 times":
if(e.target.response=="Failed CSRF check!") {
if(failedAjax<10) checkForSurvey();
failedAjax++;
return;
}
It is generally working, but errors are appears in a console and it is very dirty solution.
I am using Slim 3 microframework with CSRF extension. Really please for help with that interesting problem.
I will be very thankful,
Arthur
There are some options for you:
Use a stack of csrf-tokens inside you javascript code
Use a csrf token which is can be used more than once (not so secure)
Use a queue for the request
The Slim-Csrf
-middleware provides functionallity for you, to generate these tokens, you just need to get them to the clientside.
You could do an api for getting 5 csrf tokens, this api would also consume on csrf-token.
Add an api and generate the tokens there.
$app->get('/foo', function ($request, $response, $args) {
// check valid csrf token
$tokens = [];
for ($i = 0; $i < 5; $i++) {
$tokens[] = $this->csrf->generateToken();
}
return $response->withJson($tokens);
});
Now the csrf-token are valid through the whole user session.
Guard::generateToken()
returns something like this:
array (size=2)
'csrf_name' => string 'csrf58e669ff70da0' (length=17)
'csrf_value' => string '52ac7689d3c6ea5d01889d711018f058' (length=32)
For that, Slim-Csrf already provides functionallity with the token persistance mode. That can be enabled through the constructor or the Guard::setPersistentTokenMode(bool)
method. In my example, I'm doing this with the method:
$container['csrf'] = function ($c) {
$guard = new \Slim\Csrf\Guard;
$guard->setPersistentTokenMode(true);
return $guard;
};
Here the PhpDoc from the persistanceTokenMode
-attribute
/**
* Determines whether or not we should persist the token throughout the duration of the user's session.
*
* For security, Slim-Csrf will *always* reset the token if there is a validation error.
* @var bool True to use the same token throughout the session (unless there is a validation error),
* false to get a new token with each request.
*/
Add a queue for the request, that could be delay the execution of your request but there will always be a valid csrf token.
This should be seen as pseudocode as I havn't tested this yet.
var requestQueue = [];
var isInRequest = false;
var csrfKey = ''; // should be set on page load, to have a valid token at the start
var csrfValue = '';
function newRequest(onSuccessCallback, data) { // add all parameters you need
// add request to the queue
requestQueue.push(function() {
isInRequest = true;
// add to csrf stuff to data
$.ajax({
data: xxx
url: "serverscript.xxx",
success: function(data) {
// update csrfKey & csrfValue
isInRequest = false;
tryExecuteNextRequest(); // try execute next request
onSuccessCallback(data); // proceed received data
}
}});
);
tryExecuteNextRequest();
}
function tryExecuteNextRequest() {
if(!isInRequest && requestQueue.length != 0) { // currently no request running &
var nextRequest = requestQueue.shift();
nextRequest(); // execute next request
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With