单页应用程序的Rails CSRF保护(反应,角度,余烬)

7 javascript ajax ruby-on-rails csrf csrf-protection

好。我正式对这个问题失去了理智。

我们使用默认的Rails应用程序(5,但我也尝试使用4默认应用程序)。

我正在尝试使用一个简单的javascript代码将ajax POST请求发送到一个控制器动作。

在我的ApplicationController代码中:

class ApplicationController < ActionController::Base

  after_action :set_csrf_cookie

  protected

    def set_csrf_cookie
      cookies["X-CSRF-Token"] = form_authenticity_token
    end

end
Run Code Online (Sandbox Code Playgroud)

设置"X-CSRF-Token"值为的Cookie form_authenticity_token

之后,我可以使用以下代码在SPA(单页应用程序)中读取此cookie:

<script>
function readCookie(name) {
    var nameEQ = name + "=";
    var ca = document.cookie.split(";");
    for (var i = 0; i < ca.length; i++) {
      var c = ca[i];
      while (c.charAt(0) === " ") c = c.substring(1, c.length);
      if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
    }
    return null;
  }

// var token = document.getElementsByName('csrf-token')[0].content; // this works!
const token = readCookie("X-CSRF-Token"); // this doesn't work!

fetch('/api/v1', {
  method: 'POST',
  body: {""},
  headers: {
    'Content-Type': 'application/json',
    'X-CSRF-Token': token
  },
  credentials: 'include'
}).then(function(response) {
  return response.json();
});
</script>
Run Code Online (Sandbox Code Playgroud)

当我使用此行时:

var token = document.getElementsByName('csrf-token')[0].content;
Run Code Online (Sandbox Code Playgroud)

它之所以有效,是因为它读取了Rails在html页面中插入的内容:

<%= csrf_meta_tags %>

<meta name="csrf-param" content="authenticity_token">
<meta name="csrf-token" content="VXaKlO+/Gr/8pGhr5y0bThQ5L/0IDiznMR/9SpaoI6vOoF9KtmB5/9ka+Hz+zjyssNRi/Em/Ye27C+E5pl3odg==">
Run Code Online (Sandbox Code Playgroud)

因此,“ csrf令牌”的内容有效,并且我的Rails应用程序可以验证CSRF。

这是来自Rails的代码:https : //github.com/rails/rails/blob/v5.2.0/actionpack/lib/action_controller/metal/request_forgery_protection.rb

相反,当我使用此行:

const token = readCookie("X-CSRF-Token");
Run Code Online (Sandbox Code Playgroud)

它不起作用,我得到这个错误:

Started POST "/api/v1" for 172.18.0.1 at 2018-05-01 18:52:56 +0000
Processing by MyController#action as */*
  Parameters: {"body"=>{}}
Can't verify CSRF token authenticity.
Completed 422 Unprocessable Entity in 2ms (ActiveRecord: 0.0ms)
ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):
Run Code Online (Sandbox Code Playgroud)

另外,如果我在具有相同脚本的其他服务器(npm http-server或Microsoft IIS或其他服务器)上使用另一个页面,则问题是相同的。

如果我从Rails html页面复制“ csrf-token”的内容并在我的Javascript脚本中使用此行:

const token = "VXaKlO+/Gr/8pGhr5y0bThQ5L/0IDiznMR/9SpaoI6vOoF9KtmB5/9ka+Hz+zjyssNRi/Em/Ye27C+E5pl3odg==";
Run Code Online (Sandbox Code Playgroud)

有用!

所以我的问题是:为什么?


我读过什么(什么都没有!):

小智 6

Tnx,我让您的代码正常工作。我只需要添加decodeURIComponent()

const token = decodeURIComponent(readCookie("X-CSRF-Token"));
Run Code Online (Sandbox Code Playgroud)

我将这种方法用于带有缓存html的渐进式Web应用程序。默认的rails元标记(<%= csrf_meta_tags %>)与缓存的html不兼容。

该博客文章还提供了其他一些替代方法:https : //www.fastly.com/blog/caching-uncacheable-csrf-security


pet*_*.np 5

Rails期望的标题名称是X_CSRF_TOKEN(请注意下划线)。我看不到您共享的其余代码的问题-也许cookie中的令牌必须经过URI解码(decodeURIComponent),所以也没有问题,因此,如果仍然收到警告,请也进行检查。