Why is Rails giving me “Can't verify CSRF token authenticity” error?

六眼飞鱼酱① 提交于 2019-12-05 03:55:54

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.

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.

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:

It is common to use persistent cookies to store user information, with cookies.permanent for example. In this case, the cookies will not be cleared and the out of the box CSRF protection will not be effective. If you are using a different cookie store than the session for this information, you must handle what to do with it yourself:

rescue_from ActionController::InvalidAuthenticityToken do |exception|
  sign_out_user # Example method that will destroy the user cookies
end

The above method can be placed in the ApplicationController and will be called when a CSRF token is not present or is incorrect on a non-GET request.

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  

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).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!