【问题标题】:Rails/AJAX cached form submission - 422 unprocessable entityRails/AJAX 缓存表单提交 - 422 无法处理的实体
【发布时间】:2016-10-30 20:18:55
【问题描述】:

My Rails 应用程序具有一个面向公众的表单,它通过 AJAX 将用户输入作为字符串化 JSON 传递给控制器​​。该表单是为离线使用而设计的,因此除了第一次之外,对表单页面的每次访问都来自浏览器缓存(使用缓存清单)。我遇到了表单提交返回422 unprocessable entity 错误的问题,除非在导航到表单页面之前已清除浏览器历史记录......也就是说用户只能提交一个表单,所有后续提交都是@ 987654324@ 除非他们清除历史记录并返回表单刷新缓存。不幸的是,这不会成功。

我对 Rails 安全性没有丰富的经验,但我的印象是这与 CSRF 保护有关,而且对于除第一次之外的任何表单页面的访问,都会传递一个陈旧的 CSRF 令牌.

我的 AJAX 请求如下所示:

$.ajax({
    url: "post/submission",
    type: "POST",
    dataType: "json",
    beforeSend: function(xhr) {xhr.setRequestHeader("X-CSRF-Token", $("meta[name='csrf-token']").attr("content"))},
    data: {"post" : postParameter},
    success: function(response){
        window.location = '/post/approval';
    }
});

目前,布局页面包含<%= csrf_meta_tags %>,我在应用控制器中有标准的protect_from_forgery with: :exception

关于此表单最后要注意的结构元素是,虽然表单本身是面向公众的,但它需要用户在单击提交按钮后登录 - 所以提交不会成功没有有效的登录。

有没有一种安全的方法可以解决这个问题?我确信这是不言而喻的,但我不能让我的用户在每次提交后清除他们的历史记录并重新缓存表单。

【问题讨论】:

  • 如果你想保持 CSRF 保护,你必须在提交表单时重新生成令牌。 <%= hidden_field_tag :authenticity_token, form_authenticity_token %> 就是这样做的。从缓存中显示页面时的 ajax 调用就可以了。查看更多herehere
  • 嗯...我应该提到的另一件事是表单不是以标准 Rails 方式构建的表单助手等。事实上,表单不是完全有 HTML 意义 - 在提交时,用户输入被编译成 JSON 字符串并通过 AJAX 发送到控制器以解析出来并插入到数据库中......所以实际上没有地方放置 hidden_field_tag。我认为在您包含的第二个链接中可能有一些对我来说很好的解决方案 - 我会测试它们并让您知道我是否可以做任何事情。感谢您的宝贵时间。
  • 当然,您只需将form_authenticity_token 的结果传递给您的前端,无论它是作为模板渲染的一部分完成还是通过ajax 调用传输。从那里您将能够将其包含到您的 POST 有效负载中。
  • 好的,谢谢 - 让我看看我能做些什么。
  • @NicNilov 我将隐藏的身份验证令牌添加到页面并修改了 AJAX 以使 url 为 "post/submission" + "?&authenticity_token=" + auth_tokenauth_token 是 $("#authenticity_token") 的值),但它仍然抛出同样的错误。这是因为表格是从缓存中提供的吗?我还测试了设置protect_from_forgery with: :null_session。确实有效 - 是否有任何安全理由不这样做?

标签: ruby-on-rails ajax


【解决方案1】:

为了在从 JavaScript 提交 POST 请求时保持 CSRF 保护,您需要在表单的有效负载或请求标头中提供当前身份验证令牌。

从浏览器缓存加载页面时,其身份验证令牌可能已过时,因此需要从后端刷新。一种方法是检测"page loaded from browser cache" 事件并运行ajax 请求以获取应由后端form_authenticity_token 方法生成的新令牌。

收到的身份验证令牌随后可用于后续 JavaScript POST 请求。

描述了一种检测从缓存加载的页面的技术,例如in this StackOverflow answer.

【讨论】:

    猜你喜欢
    • 2018-06-30
    • 2015-09-09
    • 2019-03-14
    • 1970-01-01
    • 2021-05-27
    • 1970-01-01
    • 1970-01-01
    • 2019-06-14
    • 2019-07-11
    相关资源
    最近更新 更多