使用 ajax 提交表单时刷新 csrf 令牌是个好主意吗?

Is it a good idea to refresh csrf token when submitting form with ajax?

我想延长我的 CSRF 令牌有效期。我知道 laravel-caffeine 但如果页面闲置时间过长,令牌仍然会过期。(例如 24 小时以上)

所以我想出了一个想法,首先使用 ajax GET 方法检索最新的 csrf 令牌,然后使用这个刷新的令牌提交表单。

但我不确定是否存在一些安全问题。例如,假设可以在 http://example.com/get_csrf 获取最新的 csrf 令牌,并且对手也可以访问此 URL。不知道对手是否可以利用它并破坏csrf保护?

是的,您的担忧是正确的。该令牌用于防止其他网站通过受害者的会话创建对您网站的请求。通过 ajax 使令牌可请求可能会产生问题,如果可以从攻击网站完成的话。

您可以通过以下方式保护 /get_csrf 路线:

  1. 在提供表单时保存 csrf_token() 服务器端并将其保存在用户的一列中 table:
//in your controller

public function showMyForm(){
    $user=auth()->user();
    $user->last_csrf=csrf_token(); // save the csrf token
    $user->save(); //persist to database

    return view('myform');
}
  1. 让您的 get_csrf 路由验证旧令牌和 return 新令牌:
//in your other controller (Route::get('/get_csrf','Controller@getCSRF');)

public function getCSRF(Request $request){
    $user=auth()->user();
    $old_user_token=$user->last_csrf;
    $old_client_token=$request->current_token;
    if ($old_client_token && $old_user_token==$old_client_token){

        // it's a match, update the token in the users table and send the new one
        $user->last_csrf=csrf_token();
        $user->save();
        return csrf_token();
    }

    //no match, tell the client he is unauthorized to get the new csrf token
    abort(401);
}
  1. ajax GET 使用页面当前的 csrf 作为有效载荷:(在这里使用 jquery 但你不需要)
$.ajax({
    url:'/get_csrf',
    method:'POST',
    dataType:"json",
    headers:{
        // have the server treat the request as GET
        'X-HTTP-Method-Override': 'GET' 
    },
    data:{
        // send the page's current csrf in the payload
        current_token:document.querySelector('meta[name="csrf-token"]').getAttribute('content') 
    },
    success(data){

        // this is whatever the server sent back as the current csrf for this session
        new_csrf=data; 

        // update the page's csrf
        document.querySelector('meta[name="csrf-token"]').setAttribute('content',new_csrf);

        //update any _token fields
        document.querySelectorAll('input[name="_token"]').forEach(function(csrf_field){

            csrf_field.setAttribute('value',new_csrf);

        });


    },
    error(response){
        // error handling. maybe console.log(response) here to see what happened?
    }
});
  1. 像往常一样提交表单。

如果攻击是通过 iframe 进行的,js 代码可以截取受害者的 iframe 的屏幕截图,而不是解析 csrf 令牌,并且可以用于另一个请求(理论上)。