问题描述
在Rails生产中,我得到了无法验证CSRF令牌的真实性。我的问题是:
I am getting a "Can't verify CSRF token authenticity" in Rails production. My questions are:
- 为什么这样做?
- 如何解决? / li>
- Why is it doing this?
- How can I fix it?
这是我的Heroku日志(某些值已匿名):
Here's my Heroku logs (some values anonymized):
2016-02-13T01:18:54.118956+00:00 heroku[router]: at=info method=POST path="/login" host=[MYURL] request_id=[ID STRING] fwd="FWDIP" dyno=web.1 connect=0ms service=6ms status=422 bytes=1783
2016-02-13T01:18:54.116581+00:00 app[web.1]: Started POST "/login" for [IPADDRESS] at 2016-02-13 01:18:54 +0000
2016-02-13T01:18:54.119372+00:00 app[web.1]: Completed 422 Unprocessable Entity in 1ms
2016-02-13T01:18:54.118587+00:00 app[web.1]: Processing by SessionsController#create as HTML
2016-02-13T01:18:54.118637+00:00 app[web.1]: Parameters: {"utf8"=>"✓", "authenticity_token"=>"[BIGLONGRANDOMTOKENSTRING]", "session"=>{"email"=>"[FRIENDSEMAILADDRESS]", "password"=>"[FILTERED]", "remember_me"=>"0"}, "commit"=>"Log in"}
2016-02-13T01:18:54.119082+00:00 app[web.1]: Can't verify CSRF token authenticity
2016-02-13T01:18:54.120565+00:00 app[web.1]:
2016-02-13T01:18:54.120567+00:00 app[web.1]: ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):
2016-02-13T01:18:54.120569+00:00 app[web.1]: vendor/bundle/ruby/2.2.0/gems/actionpack-4.2.0/lib/action_controller/metal/request_forgery_protection.rb:181:in `handle_unverified_request'
.
.
.etc
我知道的唯一表现是当我的朋友尝试登录时在他的iPhone 5上使用Safari。他的用户帐户大约在6个月前创建。我99%确信他当时用手机访问该网站的情况还不错。从那以后,他还没有登录,并且我不知道我对登录/验证码进行的任何更改。昨天,我让他在大约6个月内首次访问我的网站,现在他收到了CSRF错误。
The only manifestation I'm aware of is when my friend tries to log in using Safari on his iPhone 5. His user account was created right about 6 months ago. I'm 99% sure that he accessed the site just fine with his phone at that time. Since then he hasn't logged in and I'm not aware of any changes I've made to the login/auth codes. Yesterday I had him hit my site for the first time in ~6 months and now he gets the CSRF error.
任何其他用户帐户都不会发生此问题(我知道)或在任何其他设备上。实际上,从他的旧版iPhone 4登录到他的帐户就可以了。
This issue doesn't happen to any other user account (that I'm aware of) or on any other device. In fact, logging in to his account from his older iPhone 4 works just fine.
我有相当不错的开发经验,但对于Web开发人员和Rails来说都是全新的
I have a decent amount of dev experience but am totally new to web dev and everything Rails.
这就是我所拥有的:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
include SessionsHelper
end
应用程序布局:
<!DOCTYPE html>
<html>
<head>
<title><%= full_title(yield(:title)) %></title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= stylesheet_link_tag 'application', media: 'all' %>
<%= javascript_include_tag 'application' %>
<%= csrf_meta_tags %>
<%= render 'layouts/shim' %>
</head>
<body>
<%= render 'layouts/header' %>
<div class="container">
<% flash.each do |message_type, message| %>
<div class="alert alert-<%= message_type %>"><%= message %></div>
<% end %>
<%= yield %>
<%= render 'layouts/footer' %>
<%= debug(params) if Rails.env.development? %>
</div>
</body>
</html>
我的机密文件如下:
# Do not keep production secrets in the repository,
# instead read values from the environment.
production:
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
我在Heroku上有一个secret_key_base的生产环境var。
And I have a production environment var on Heroku for the secret_key_base.
def log_in(user)
session[:user_id] = user.id
end
def remember(user)
user.remember
cookies.permanent.signed[:user_id] = user.id
cookies.permanent[:remember_token] = user.remember_token
end
这是我所做的:
我开始开发遵循中的所有内容,直到第10章为止。相关的是第8章。具体来说,我的应用程序使用的所有安全性/ Cookie /用户身份验证内容与tut中的完全相同。我没有在应用程序中做任何花哨的事情……没有AJAX或类似的东西。我什至取消了turbolinks。
Here's what I've done:
I began developing my app by following everything in Michael Hartl's Rails Tutorial to the letter up to/through Chapter 10. Most relevant is Chapter 8. Specifically, my app uses all the security / cookies / user auth stuff exactly as in the tut. I don't do anything fancy in my app...no AJAX or anything like that. I even yanked turbolinks.
我的项目已经过去了18个月,所以我不是100%肯定开始使用哪个版本的。我知道它是4.1.X,可能是4.1.6。我也不确定我的升级日期,但在某些时候确实符合我目前的运行方式; 4.2.0。
My project has spanned the last 18 months so I'm not 100% positive which version I began on. I know it was 4.1.X and it was probably 4.1.6. I also am unsure the date I upgraded but at some point did to what I'm presently running; 4.2.0.
我几乎阅读了我在网上可以找到的有关CSRF + Rails问题的每条帖子。似乎对于我已阅读的几乎所有内容,原因和解决方案都与AJAX或Devise有关,但两者均不适用于我。 iFrame问题是网络上的另一个常见来源,我都没有使用过。
I've read just about every post I can find on the web regarding problems with CSRF + Rails. Seems like for almost everything I've read, the cause and solution have to do with AJAX or Devise, neither of which apply to me. iFrame issues are another common source on the web, which I'm neither using.
我没有使用我的应用的密码重置功能。我尝试将protect_from_forgery更改为::reset_session。唯一的更改是不再显示Rails异常页面。但这不会让他进入任何需要身份验证的页面。只是让他重新扎根,因为我的路线中有这行:
I've used my app's password reset feature to no avail. I tried changing protect_from_forgery to with: :reset_session. The only thing this changes is the Rails exception page is no longer displayed. But it won't let him go to any page requiring authentication. It just takes him back to root because I have this line in my routes:
get '*path' => redirect('/')
我不想清除他的cookie /缓存等,因为我有
I don't want to clear his cookies/cache etc because I have dozens of other existing user accounts that I don't want to have to manually fix.
经常建议的解决方案是关闭安全性的一种变体,我不希望手动修复。出于明显的原因。
Frequently suggested solutions are some variant of turning off security, which I don't want to do for obvious reasons.
我做了一些其他更改,但还没有机会进行测试(因为我无法轻松访问朋友的iPhone) ):
Some other things I have changed but haven't had a chance to test yet (because I don't have easy access to my friend's iPhone):
我在session_store.rb中更改了应用商店名称:
I changed the appstore name in session_store.rb:
Rails.application.config.session_store :cookie_store, key: '[NEWNAME]'
运行以下命令命令:
heroku运行rake资产:clean
heroku运行rake资产:precompile
Ran the following commands:
heroku run rake assets:clean
heroku run rake assets:precompile
我即将着手,尤其是第3部分。
I am about to embark on a deep dive here, especially section 3.
感谢您的阅读/考虑。任何提示/想法/建议/指针将不胜感激!
Thanks for reading/considering. Any tips/ideas/suggestions/pointers would be greatly appreciated!
推荐答案
结果是与我的问题完全相同,并且能够创建一个对我有用的repro案例。如果我理解正确,那么Safari会在缓存页面,但会浪费会话时间。这会导致authenticity_token值在我的rails参数中看起来合法,但是在验证令牌时由于会话是nuked,protect_from_forgery失败。
Turns out this gentleman was having the exact same issue as me and was able to create a repro case that worked for me. If I'm understanding right, Safari is caching the page but nuking the session. This causes the authenticity_token value to look legit'ish in my rails params but protect_from_forgery fails when verifying the token because the session was nuked.
解决方案有两个:关闭缓存并处理CSRF异常。即使您关闭了缓存,您仍然需要处理异常,因为某些浏览器(例如Safari)不遵守无缓存设置。在这种情况下,会出现CSRF问题,因此也需要处理该问题。
The solution then is two fold: turn off caching and handle CSRF exceptions. You still need to handle exceptions even if you turn off caching because some browsers (e.g. Safari) don't respect no-cache settings. In this case a CSRF issue arises and hence the need to handle that also.
对我来说,解决方法是通过杀死所有cookie和会话数据来处理CSRF异常,闪烁哎呀消息,然后将其重定向到登录页面。重定向将下拉一个新的身份验证令牌,该令牌将在进行登录后进行验证。这个想法来自:
The workaround for me was to handle the CSRF exception by killing off all my cookies and session data, flash an "oops" message and redirect them to the login page. The redirect will pull down a fresh auth token that will verify when doing the login post. This idea came from here:
rescue_from ActionController::InvalidAuthenticityToken do |exception|
sign_out_user # Example method that will destroy the user cookies
end
cookies.permanent正是我所使用的。因此,我实现了上述提示,例如:
cookies.permanent is exactly what I was using. So I implemented the above tip like this:
class ApplicationController < ActionController::Base
include SessionsHelper
protect_from_forgery with: :exception
before_filter :set_cache_headers
rescue_from ActionController::InvalidAuthenticityToken do |exception|
cookies.delete(:user_id)
cookies.delete(:remember_token)
session.delete(:user_id)
@current_user = nil
flash[:danger] = "Oops, you got logged out. If this keeps happening please contact us. Thank you!"
redirect_to login_path
end
def set_cache_headers
response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate"
response.headers["Pragma"] = "no-cache"
response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"
end
end
顺便说一句,在实施此修补程序并验证它在dev中工作后,尽管行为不同,但我朋友的手机仍无法登录。经调查,我发现他的iPhone 5 Safari设置中所有cookie被阻止。这导致了其他奇怪的行为,使得很难弄清是什么原因导致了什么。当我意识到我无法使用他的手机登录任何在线帐户(例如yahoo邮件等)时,举报便来了。进入他的Safari设置并允许cookie解决了问题,现在一切在他的手机上(以及我知道的所有其他地方)都可以正常使用。
Incidentally, after implementing the fix and verifying it worked in dev, my friend's phone was still unable to login, albeit with different behavior. Upon investigation I found he had "all cookies blocked" in his iPhone 5 Safari settings. This caused other weird behavior that made it hard to sort out which issue was causing what. The tipoff came when I realized I couldn't use his phone to log in to any online accounts (e.g. yahoo mail etc.). Going in to his Safari settings and allowing cookies solved things and now everything works great on his phone (and everywhere else that I'm aware of).
这篇关于为什么Rails会给我“无法验证CSRF令牌的真实性”?错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!