简体   繁体   English

许多AJAX请求同时使用CSRF保护

[英]Many AJAX requests at once with CSRF protection

Hi everybody. 大家好。

My web application is based on asynchronous requests. 我的Web应用程序基于异步请求。 Timer widget is working and updating it's status every second by AJAX (yes, it is necessary). 定时器小部件正在工作,并且每秒都通过AJAX更新它的状态(是的,这是必要的)。

I am sending with each AJAX my CSRF tokens: 我发送每个AJAX我的CSRF令牌:

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. 但是,如果我将另外的AJAX作为例子当我将todo列表中的任务更改为“完成”时,它有时会以错误结束,因为我在从前一个请求获取新令牌之前发送AJAX。

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. 第一个想法是我将使用5个不同的令牌制作“像堆栈数组”,但是一个https请求=一对令牌,我无法生成它。

Maybe some type of queue of ajax requests, but what with doing them in a right time - I don't know. 也许某种类型的ajax请求队列,但是在适当的时候做什么 - 我不知道。

My actual pseudo-solution is "if failed try again max 10 times": 我的实际伪解决方案是“如果失败再次尝试最多10次”:

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. 我正在使用带有CSRF扩展的Slim 3微框架。 Really please for help with that interesting problem. 真的请帮助解决这个有趣的问题。

I will be very thankful, 我会非常感激,

Arthur 亚瑟

There are some options for you: 有一些选择:

  1. Use a stack of csrf-tokens inside you javascript code 在javascript代码中使用一堆csrf-tokens

  2. Use a csrf token which is can be used more than once (not so secure) 使用可以多次使用的csrf令牌(不太安全)

  3. Use a queue for the request 为请求使用队列

A stack for the tokens 令牌的堆栈

The Slim-Csrf -middleware provides functionallity for you, to generate these tokens, you just need to get them to the clientside. Slim-Csrf -middleware为您提供功能,生成这些令牌,您只需将它们带到客户端即可。 You could do an api for getting 5 csrf tokens, this api would also consume on csrf-token. 你可以用api获得5个csrf令牌,这个api也会消耗csrf-token。

Add an api and generate the tokens there. 添加一个api并在那里生成令牌。

$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. 现在,csrf-token在整个用户会话中都有效。

Guard::generateToken() returns something like this: Guard::generateToken()返回如下内容:

array (size=2)
  'csrf_name' => string 'csrf58e669ff70da0' (length=17)
  'csrf_value' => string '52ac7689d3c6ea5d01889d711018f058' (length=32)

A multi-use csrf-token 多用途csrf令牌

For that, Slim-Csrf already provides functionallity with the token persistance mode. 为此,Slim-Csrf已经提供了令牌持久性模式的功能。 That can be enabled through the constructor or the Guard::setPersistentTokenMode(bool) method. 这可以通过构造函数或Guard::setPersistentTokenMode(bool)方法启用。 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 这里是persistanceTokenMode -attribute的PhpDoc

/**
 * 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.
 */

A queue for the ajax requests. ajax请求的队列。

Add a queue for the request, that could be delay the execution of your request but there will always be a valid csrf token. 为请求添加一个队列,这可能会延迟执行您的请求,但总会有一个有效的csrf令牌。

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
    }
}

Generally, you can simply eliminate CSRF by not accepting cookies for authentication. 通常,您可以通过不接受cookie进行身份验证来简单地消除CSRF。

You can save the authentication token in the localStorage and send it as an header with each request. 您可以将身份验证令牌保存在localStorage中,并将其作为每个请求的标头发送。

This way you'll never need to worry about CSRF and its tokens. 这样您就不必担心CSRF及其令牌。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM