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.1
rails 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
发送表单的部分原因。
开箱即用的导轨 <%= forms_* %>
为您执行此操作,因此您可能有一些自定义的东西。
如果您从 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! -->
...
【讨论】:
看起来我有两个类似的标签。<%= csrf_meta_tags %>
和<%= csp_meta_tag %>
我还注意到每个表单都会失败,即使是与身份验证无关的表单。更新我的帖子。我有一种非常强烈的感觉,这甚至与 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 -- Jquery 在另一个 post 请求之前没有提交 put 请求