“警告:无法验证CSRF令牌真实性”错误 - 使用Devise的CORS和:token_authenticatable

时间:2012-10-24 05:23:21

标签: ruby-on-rails ajax devise cors

我有一个单页应用程序,使用CORS对另一个域进行身份验证。所有请求都是JSON请求。

我的应用可以验证正常,可以让GET请求正常。身份验证使用token_authenticatable。即所有请求附加'?auth_token = whatever'

所以,我的实际问题是,当我尝试执行PUT请求时,我在rails日志中获得警告:无法验证CSRF令牌真实性消息以及 CanCan :: AccessDenied(您无权访问此页面。)例外。

只需将skip_before_filter :verify_authenticity_token添加到rails控制器即可解决问题。

因此我只能断定我的ajax请求发送的是无效或空的csrf_token。

我真的不明白这是怎么回事,因为我相信我正确地为每个ajax请求正确发送了X-CSRF-Token标头。

基本上,我的应用验证并且Devise发回一个auth_token和一个csrf_token:

render :status => 200, :json => {
  :auth_token => @user.authentication_token,
  :csrf_token => form_authenticity_token
}

然后我将这些令牌存储在我的ajax应用程序中,并在jQuery中使用ajaxSend,设置它以便jQuery为每个请求传递这些令牌:

initialize: ->
  @bindTo $(document), 'ajaxSend', @appendTokensToRequest

appendTokensToRequest: (event, jqXHR, options) ->
  if not @authToken? then return

  if @csrfToken?
    jqXHR.setRequestHeader 'X-CSRF-Token', @csrfToken

  if options.type is 'POST'
    options.data = options.data + (if options.data.match(/\=/) then '&' else '') +
    $.param auth_token:@authToken
  else
    options.url = options.url + (if options.url.match(/\?/) then '&' else '?') +
    $.param auth_token:@authToken

然后,我可以在Chrome网络标签中看到,每个GET请求都会发送auth_token参数,以及X-CSRF-Token标题。

然而,在PUT请求中它似乎没有工作。

我的理论是CORS正在填补空白。如果您发出了CORS请求,那么您的浏览器实际上会首先发出附加 OPTIONS请求,以检查您是否有权访问此资源。

我怀疑OPTIONS请求没有传递X-CSRF-Token标头,因此rails会立即使rails端的csrf_token无效。然后,当jQuery发出实际的PUT请求时,它传递的csrf_token不再有效。

这可能是问题吗?

我能做些什么来证明这一点? Chrome似乎没有向我显示网络标签中的OPTIONS请求,以帮助我调试问题。

这不是一个重大问题,因为我可以关闭CSRF。但我想知道它为什么不起作用。

3 个答案:

答案 0 :(得分:1)

我认为你需要处理OPTIONS请求,它应该响应允许CORS请求的各种头,IIRC它们是access-control-allow-method,access-control-allow-origin和access - 控制 - 允许报头。由于OPTIONS请求失败,因此可能没有发生PUT请求。

答案 1 :(得分:1)

我刚遇到同样的问题。问题是无法在CORS中发送using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Xml; using System.Xml.Serialization; using System.IO; namespace ConsoleApplication1 { class Program { const string FILENAME = @"c:\temp\test.xml"; static void Main(string[] args) { Root root = new Root() { baseClass = new List<BaseClass>(){ new InheritorClass1(){ name = "class1"}, new InheritorClass2(){ name = "class2"} } }; XmlSerializer serializer = new XmlSerializer(typeof(Root)); StreamWriter writer = new StreamWriter(FILENAME); serializer.Serialize(writer, root); writer.Flush(); writer.Close(); writer.Dispose(); XmlSerializer xs = new XmlSerializer(typeof(Root)); XmlTextReader reader = new XmlTextReader(FILENAME); Root newRoot = (Root)xs.Deserialize(reader); } } [XmlRoot("Root")] public class Root { [XmlElement("BaseClass")] public List<BaseClass> baseClass { get; set; } } [XmlRoot("BaseClass")] [XmlInclude(typeof(InheritorClass1))] [XmlInclude(typeof(InheritorClass2))] public class BaseClass { [XmlElement("name")] public string name { get; set; } } [XmlRoot("InheritorClass1")] public class InheritorClass1 : BaseClass { } [XmlRoot("InheritorClass2")] public class InheritorClass2 : BaseClass { } } ​ cookie。因此,当Rails尝试验证令牌时,_session_idsession[:_csrf_token],Rails会在比较之前生成新的令牌。

要解决此问题,您需要在CORS中启用cookie发送。这是Mozilla Developer Network reference。服务器和客户端都需要工作才能使其正常工作。

<强>客户端 - 请参阅您的客户技术文档。

服务器 - 在对预检(HTTP OPTIONS)调用的响应中将标题null设置为Access-Control-Allow-Credentials(字符串)。

答案 2 :(得分:0)

在Rails中,每个表单提交都需要CSRF令牌真实性。 它用于安全地提交表单。 当我们打开应用程序时,CSRF令牌(每次)将在rails中新创建。 如果CSRF令牌未通过我们的控制器内部,则此警告将显示。 我们需要在所有表单提交中传递此令牌。