Ruby on Rails:提交表单时“您想要的更改被拒绝”

Posted

技术标签:

【中文标题】Ruby on Rails:提交表单时“您想要的更改被拒绝”【英文标题】:Ruby on Rails : "The change you wanted was rejected" on Form Submission 【发布时间】:2021-09-26 05:17:59 【问题描述】:

ruby 3.0.1rails 6.1.2'devise', '~> 4.7', '>= 4.7.3'

我的处境很不寻常。我正在将 Rails 安装从一台服务器迁移到另一台服务器。我相信我已经完成了大约 95% 的工作,刚刚恢复了生产数据库。

但是,任何涉及表单提交的事情,包括用户注册和登录,都会给我错误页面:

The change you wanted was rejected.

Maybe you tried to change something you didn't have access to.

服务器日志给了我更多帮助:

Completed 422 Unprocessable Entity in 2ms (Allocations: 433)
FATAL -- ActionController::InvalidAuthenticityToken

这让我很困惑。因为我确实重新生成了 master.key 和 credentials.yml.enc 并通过 RAILS_MASTER_KEY 环境变量使 master.key 的内容可用。这意味着表单包含正确的<input type="hidden" name="authenticity_token" value="<removed for stack_overflow>"> 以防止跨站点脚本攻击。

我认为这与会话无关,因为即使用户注册也会受此影响。我正在使用 Devise 进行身份验证。

但是……现在我碰到了一堵砖墙。从这里无处可去。有谁知道怎么回事?

更新 1

添加skip_before_action :verify_authenticity_token 确实让我跳过了这个问题。作为解决方案,我不满意。

更新 2

我有这些元标记。

<%= csrf_meta_tags %>
<%= csp_meta_tag %>

【问题讨论】:

您清除浏览器cookies并重试了吗? 我刚刚尝试这样做。不过,问题仍然存在。如果这是解决方案,我会大吃一惊的。 你在用devise吗? 是的,我会将其添加到我的帖子中,以防对其他人有所帮助。 这可能会有所帮助:gist.github.com/db0sch/19c321cbc727917bc0e12849a7565af9,我阅读并看到了一个重要说明:“..devise.rb 有问题。我只是取消注释了 secret_key = ... 只是运行时间重新生成凭据文件的命令,然后再次注释掉该行”。 【参考方案1】:

您的表单未随请求一起发送 X-CSRF-TOKEN 标头。此标头是一项安全功能,您对关闭它感到不舒服是对的。这是阻止evil.com 上的某人向yourwebsite.co 发送表单的部分原因。

开箱即用的导轨 &lt;%= forms_* %&gt; 为您执行此操作,因此您可能有一些自定义的东西。

如果您从 javascript 发送这些请求。我会这样做:

let token = document.querySelector("meta[name='csrf-token']").content
fetch(url, 
    method,
    headers: 
        'X-CSRF-Token': auth_token, // <---- this
        'X-Requested-With': 'XMLHttpRequest',
        ...
    ,
...

也有可能您没有将其包含在您的 application.html.erb 中,因此表单无法找到它。

<!DOCTYPE html>
<html>
<head>
  <%= csrf_meta_tags %> <!-- you need this! -->
...

【讨论】:

看起来我有两个类似的标签。 &lt;%= csrf_meta_tags %&gt;&lt;%= csp_meta_tag %&gt; 我还注意到每个表单都会失败,即使是与身份验证无关的表单。更新我的帖子。我有一种非常强烈的感觉,这甚至与 Rails 无关。令牌正在正确发送。我接下来要调查的是我的 nginx 配置。感谢您抽出宝贵时间回复。 请注意,您应该同时保留 CSP 和 CSRF。 CSP 是内容安全策略,用于控制内容的来源(例如,我应该加载来自 evil.com 的脚本还是阻止它),CSRF 有助于防止其他网站上的 js 向您的域发出请求。 感谢您的详细解答。即使结果不是我需要的解决方案,我仍然很感激时间。我觉得我终于明白了为什么该令牌首先存在的目的。【参考方案2】:

我不好意思承认。但是 nginx 没有正确配置。一旦我到达了 Rails 的临界点,我终于开始问这怎么可能发生。

这是交易。在内部,rails 使用 http 来访问各种端点。我有一个 nginx 重定向块,如下所示:

 server 
  listen 80 default_server;

  server_name _;

  return 301 https://$host$request_uri;

这意味着每次在 http 上点击内部端点时,它都会变成带有 301 的 https ...始终是 GET。一旦我在production.rb 中使用了config.force_ssl = true,我只需要重做我的ngix 配置来支持它。

这是工作配置。

location / 
  proxy_set_header Host $http_host;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header X-Forwarded-Proto https;
  proxy_redirect off;
  proxy_pass http://app:3000;

它看起来像身份验证令牌问题的原因是,一旦请求作为 GET 发送,它就是一个不可处理的实体。即使我可以在浏览器检查中看到它,令牌到达那里时也可能是 nil 或其他东西。只是动词错了。

【讨论】:

顺便说一下,代理到本地端口并不理想(这也是我以前运行它的方式 xD)apache 有 mod_passenger,这对 rails 非常有用,我认为它们也支持 nginx。 phusionpassenger.com/library/config/nginx/intro.html 最好运行一个真正的应用服务器与 nginx 集成。

以上是关于Ruby on Rails:提交表单时“您想要的更改被拒绝”的主要内容,如果未能解决你的问题,请参考以下文章

Ruby on Rails:以表单提交数组

不允许用户在 ruby​​ on rails 中提交带有空字段的表单

Ruby on rails -- Jquery 在另一个 post 请求之前没有提交 put 请求

如何使用用户定义的字段数制作表单? - Ruby on Rails

如何在 ruby​​ on rails 中发布数据

使用 js/jquery 在 ruby​​ on rails 中隐藏 form_tag 上的提交按钮