For a couple of months now I've been using an action called update_snapshot in my controller.
Essentially this action tells the server to connect to a webcam and capture/save a jpeg from it that someone uses to visually review a remote location is fully operating as expected.
But anyhow...
Today I started seeing WARNING: Can't verify CSRF token authenticity in my logs and the user get's logged out
Now I understand that you need to send a CSRF token with a request, but the issue here is that I am.
This is definitely a brand new issue that happened today and I'm not sure why it's suddenly an issue.
In order to resolve the issue for now I am skipping the before_filter for :verify_authenticity_token for this action (and only this action).
I think I can keep it like this without any real issue, but any idea as to why it would suddenly start happening, and why only on this action?
Figured out the issue here.
I'd somewhat recently added action caching for my show action.
The link that does the post is in this cached view.
The CSRF meta-tag needs to be updated by making a separate ajax request to another, uncached controller that just serves this tag.
Related
I know what CSRF protection is, I know how it works in the context of a Rails app, and I know how to "solve" my problem, e.g., by disabling it.
However, I'd like to keep CSRF protection turned on. My question is, what would cause the error:
Can't verify CSRF token authenticity
To appear overnight? Nothing has been pushed to production (i.e., not a code issue, and FYI I'm not using Devise). Still using the same browser (Chrome on desktop) which has not been updated. Using the same form (a template that every user uses). Literally yesterday it was all working fine, and today it suddenly isn't.
Only guessing possible here I guess.
Precondition: There is no bug ;-) Guess: A real CSRF attack is going on.
Precondition: Not yet checked twice. Guess: Something had been modified on production. Maybe it was not even pushed, but directly changed.
Precondition: Production site is not heavily used. Guess: It was broken since longer than the night before. Nobody noticed, since all they did was logging in and triggering some 'GET' requests, and not the template form.
"csrf-token" or "csrf_token"? Sometimes GET and POST requests modify the variable name. Especially if you disable Javascript and the original post becomes a get. Of course that would be a different route, but still might throw the csrf error.
In my Rails + Devise app I have a table of links to multiple "contacts", each being a simple jquery $.get AJAX request which calls Contact#show.
Inevitably after clicking anywhere from 3-25 of the links (successfully!), a request will fail (response status 0 or failed to load resource depending on browser), after which it will never work again until the browser tab is closed or cache is cleared.
Here's the javascript for the request
$.get('/contacts/1312')
Details...
I do have csrf_meta_tags at the top of my layout
The request heads do include a "X-CSRF-Token" with the correct CSRF token from the meta tag
On chrome, the failed requests do not show up in the server logs
as a request. its as if they never made it. The only error reported
is in the chrome console which reports a failure. This leads me to believe it is browser related.
On Safari, upon first failure, it seems to destroy the session any subsequent requests result in a request for the sign_in page, which leads me to believe it may have something to do with devise
Update 3/30/13: After looking at many of the related question on SO (this one: Rails not reloading session on ajax post), which have to do with CSRF not being set correctly, I dont believe this issue is related to CSRF because it works properly several times before it fails.
i eventually did figure it out. I was using a feature datatables.js (a table library) that saved its state in the cookie. However, the data it was trying to save in the cookie exceeded the 4kb max, so my cookie was getting messed up , resulting in different behaviours across different browsers.
In our Rails application, it is common for users to keep multiple browser tabs open for hours or days at a time. The problem occurs when in one of these tabs, the user logs out then logs back in (or the session expires and a new session is created).
This causes the CSRF authenticity tokens on all the other tabs to become invalid. If they try to submit any form or make any ajax request on those tabs without refreshing, they will get an error (and in fact get logged out because that is the default Rails behavior when a bad authenticity token is passed).
This behavior is clearly undesirable. I was wondering what people do to gracefully handle situations where a user has a window open to your site but the authenticity token is out of date.
What I don't want to do is just redirect them to the login page, because then they might lose their work, if for example they have been writng a long blog post or something.
The solution that comes to mind is to have some javascript that either polls the server to check whether the authenticity token has changed, or polls the user's cookies to check whether the session has changed. I have never heard of anyone doing either of these, so I wanted to see what the community thought.
First of: logging in/out/in won't lead to appearing a new csrf-token. It still will be saved in the user's cookie. Next time it logs in via the same browser it'll get the same token.
In latest versions of Rails no errors will be thrown in the case of incorrect token: all the Rails does -- just resets the session before passing it to a controller.
So, update your Rails and you'll get one pain less.
Are you sure you are talking about CSRF token and not session token? It does not make any sense at all to redirect to login on a CSRF token mismatch. You just tell the user to repeat whatever he tried to do. (In a traditional web application this typically comes up when a form is submitted; you can treat the CSRF mismatch as a validation error, and show the form again, keeping all the field values, and ask the user to resubmit. In a more AJAX-heavy application you can use some sort of generic CSRF flag in the response, and if it is set, ask the user to do whatever he did (press the button etc) once more, or even automate the whole thing without bothering the user.
I want to grant users access to my API (hosted on heroku.com) from their sites.
But a strange problem occurs, when i want them to allow to post to the api:
Data sent from an correct form with the correct action-url (e.g. "http://myapp.com/projects/123/tasks/321/todos") - the params get serialized and send via jQuery - i encounter an "ActionController::MethodNotAllowed" with the additional info: "Only get and post requests are allowed", that re-routes to ApplicationController#index with :method => :options.
Rails doesnt extract the params for project_id (123) and task_id (321) from the url, neither are any further request_parameters, path_parameters or query_parameters available.
This behaviour occurs, when I POST from external sites, but doesn't occur, when posting from an html-page on my local machine. My first thought was about wrong encoding, but how to fix that problem.
Edit:
I am using authlogic (not devise :-D) and for the :create action the protect_from_forgery is already skipped.
Any suggestions appreciated
i guess that happens because rails tries to protect your form CSRF attacks.
you can comment out the protect_from_forgery line in your ApplicationController to test it.
but im not sure if thats the right way of dealing with this issue in the production environment.
Okay. I'll try and answer the right question this time (see other comment).
So I've thought about this, and I'm wondering, is this a case of the jQuery call attempting a PUT request? When you use the local form Rails will POST the data, but add the extra _method field to emulate a PUT.
Don't know if you are using jquery-rails, but this takes care of setting the _method parameter, and the PUT/POST verb translation for you in your AJAX calls.
The problem occured due to the cross domain policy - the request was made from another domain - and because I was using a recent browser that supports CORS, it was sending an OPTIONS-request first to get known from the server, which actions are allowed for this domain.
I use Rails request_forgery_protection mechanism to protect my POST actions from CSRF attacks and captcha to protect the GET actions. This way if someone stages a two-phase attack within one session, GET-ting the form with the current token and then POST-ing a forged request with that token, he will eventually be faced with a captcha check.
I'm stuck with that though, because Rails doesn't regenerate the CSRF token until the end of session. That doesn't seem right to me, I'd think the token should be renewed before the next action. I'm wondering maybe I have tweaked something wrong? Is there another way of doing this?
Thanks.
I've not sure if this is a good idea or not, but you can nil out the token yourself on get requests from inside your application controller.
before_filter :reset_csrf
private
def reset_csrf
session[:_csrf_token] = nil if request.get?
end
In case form token is NOT regenerated for each page request, this protection is bad. I faced it sometime ago (when testing Redmine, which is RoR-based) and reported this issue, but didn't retested it.
If it's still not regenerated, I suggest you report this to RoR team.