Laravel CSRF 保护下的 JQuery AJAX 跨站请求

Posted

技术标签:

【中文标题】Laravel CSRF 保护下的 JQuery AJAX 跨站请求【英文标题】:JQuery AJAX cross-site request under Laravel CSRF protection 【发布时间】:2015-10-06 02:32:55 【问题描述】:

我正在使用 Laravel(后端)和 ReactJS 和 JQuery(前端)构建一个类似 CMS 的 Web 应用程序。

我决定将现有的 Web API 放到一个单独的域 (api.test.com) 中,而我的用户界面在不同的域 (test.com) 上。

test.com 上,我向 api.test.com 发起 ajax 请求以修改服务器上的某些资源:

  $.ajax(
    url: "api.test.com",
    method: 'POST',
    data: ...
    success: function (no) 
    // ...
    
  );

当然,由于安全问题,这是非法的。但是,我可以配置我的网络服务器:

对于 nginx

  add_header Access-Control-Allow-Origin http://test.com;
  add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE;
  add_header Access-Control-Allow-Headers X-Requested-With,X-CSRF-TOKEN,X-XSRF-TOKEN;

Access-Control-Allow-Origin问题完美解决了,但是由于Laravel的CSRF保护又出现了一个问题...

默认情况下,Laravel 需要在请求中包含 CSRF 令牌(POST,PUT...这将修改资源)。

就我而言,我必须在 api.test.com 而不是 test.com 上生成 csrf_token,因为不同的域不共享令牌。

我遵循 Laravel 的用户指南并将这些代码添加到我的前端:

  $.ajax(
    url: "api.test.com/token", // simply return csrf_token();
    method: "GET",
    success: function (token) 
      // Now I get the token
      _token = token;
    .bind(this)
  );

并修改之前的请求实现:

  $.ajax(
    url: "api.test.com",
    method: 'POST',
    headers: 
      "X-CSRF-TOKEN": _token // Here I passed the token
    ,
    data: ...
    success: function (no) 
    // ...
    
  );

但是 Laravel 响应的状态码是 500。然后我检查了 VerifyCsrfToken.php

protected function tokensMatch($request)

    $token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');
    if (!$token && $header = $request->header('X-XSRF-TOKEN')) 
        $token = $this->encrypter->decrypt($header);
    
    // Log::info($request->session()->token() . " == $token");
    return Str::equals($request->session()->token(), $token);

我 'POST' 到的 $token 不同于原来的 ($request->session()->token()强>)。

我发现调用$.ajax时服务器上的验证令牌不同。

我尝试将两个请求放在同一个会话中(通过更改 cookie),但这是不可能的。

我花了很多时间来解决这个问题,但没有解决。

有什么想法或解决方案吗?

谢谢, 米库兹

【问题讨论】:

我将向 VerifyCsrfToken 添加代码,使您的 API 路由无需 CSRF 令牌。 【参考方案1】:

感谢您回答我的问题。我考虑过禁用对某些 URI 的 CSRF 保护,但我不想冒这些风险。

我的问题的关键是$.ajax在请求之前忘记携带cookie,导致令牌验证失败。

现在我设置了 JQuery Ajax,让它在发出请求之前携带 cookie。

  $.ajaxSetup(
    xhrFields:  withCredentials: true 
  );

和 Nginx 配置:

  add_header Access-Control-Allow-Credentials true;

顺便说一句,不必在数据中包含令牌:(Form)。

所有的问题都解决了,对我来说效果很好。

【讨论】:

【参考方案2】:

Laravel 期望令牌作为数据变量,包含在您的字段中,变量的名称需要为 _token 尝试更改它。

另一种解决方案是将令牌包含在您的数据中,而不是在标题中。

  $.ajax(
    url: "api.test.com",
    method: 'POST',
    data:  _token : _token 
    success: function (no) 
    // ...
    
  );

【讨论】:

是的,我已经尝试过这个解决方案,但没有奏效。我想我从 api.test.com 得到了一个错误的令牌,因为 $.ajax 进行了两个不同的会话。【参考方案3】:

你可以关注这个网址

http://laravel.io/forum/11-14-2014-disabling-the-csrf-middleware-in-laravel-5

在这个链接中,你需要用一个新的类来包装 VerifyCsrfToken 类,你可以在其中指定你不想使用 csrf_token 的操作

【讨论】:

以上是关于Laravel CSRF 保护下的 JQuery AJAX 跨站请求的主要内容,如果未能解决你的问题,请参考以下文章

laravel csrf保护

在 Laravel 中设置 CSRF 保护的更好方法是啥?

Laravel CSRF 保护

Laravel CSRF 保护与 REST API

laravel CSRF 保护

laravel5.2总结--csrf保护