Rails: Can't verify CSRF token authenticity when making a POST request
Cross site request forgery (CSRF/XSRF) is when a malicious web page tricks users into performing a request that is not intended for example by using bookmarklets, iframes or just by creating a page which is visually similar enough to fool users.
The Rails CSRF protection is made for "classical" web apps - it simply gives a degree of assurance that the request originated from your own web app. A CSRF token works like a secret that only your server knows - Rails generates a random token and stores it in the session. Your forms send the token via a hidden input and Rails verifies that any non GET request includes a token that matches what is stored in the session.
However in an API thats intended to be used cross site and even serve non-browser clients its not very useful due to the problems with cross-domain cookies and providing CSRF tokens.
In that case you should use a token based strategy of authenticating API requests with an API key and secret since you are verifying that the request comes from an approved API client - not from your own app.
You can deactivate CSRF as pointed out by @dcestari:
class ApiController < ActionController::Base
protect_from_forgery with: :null_session
end
Updated. In Rails 5 you can generate API only applications by using the --api
option:
rails new appname --api
They do not include the CSRF middleware and many other components that are superflouus.
- http://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf
- https://labs.kollegorna.se/blog/2015/04/build-an-api-now/
- WARNING: Can't verify CSRF token authenticity rails
WARNING: Can't verify CSRF token authenticity rails
You should do this:
Make sure that you have
<%= csrf_meta_tag %>
in your layoutAdd
beforeSend
to all the ajax request to set the header like below:
$.ajax({ url: 'YOUR URL HERE',
type: 'POST',
beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))},
data: 'someData=' + someData,
success: function(response) {
$('#someDiv').html(response);
}
});
To send token in all requests you can use:
$.ajaxSetup({
headers: {
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
}
});
Rails shows WARNING: Can't verify CSRF token authenticity from a RestKit POST
You can safely remove the warnings with the following:
skip_before_filter :verify_authenticity_token
This should go into every Rails API controller that you have, or if you have a base_controller
for all API controllers then put it there.
If you can also access your app through a web browser then do not put this line in the application_controller
as you will be creating a security vulnerability.
It is safe to remove csrf
for API calls as the particular vulnerability can only be executed through a web browser.
Update 16th December 2013
I've seen some links to this answer and some other content which suggests a clarification. An API may be vulnerable to CSRF if you use web based authentication methods to authenticate the API - e.g. sessions or cookies.
There is some good detail in Is your Web API susceptible to a CSRF exploit?.
My advice still stands for users of RestKit as user credentials are unlikely to be based on sessions or cookies but rather usernames or api keys.
If your API can be authenticated with session or cookies then you should avoid skipping : verify_authenticity_token
and you should think about moving to api key based authentication.
If your API can be authenticated with a username and password that is also used to authenticate on the web there is still a potential exploit, although it is less serious as it would require the user to type in their username and password to your site in the HTTP Auth challenge box while visiting the site with the exploit. Again, for the best security you should think about moving to api key based authentication.
It's worth noting that I don't agree that you need to add :only => [:your_method]
for additional protection, provided that you have isolated api controllers, your api is not mixed with your web responses and you are not using session or cookies. If these are in place you can safely add the skip_before_filter
into a base_controller
for your api.
Can't verify CSRF token autenticity
You need to call protect_from_forgery
at the very top of your controller, or you can remove the protect_from_forgery
call in ApplicationController
.
Example:
class ReservationsController < ApplicationController
before_action :authenticate_user!, except: [:notify]
skip_before_filter :verify_authenticity_token
protect_from_forgery except: [:notify, :your_trips]
# actions go below here
Note that you can pass multiple actions to the :except
parameter
Side note: if you are working on an JSON API only app, I suggest you to look into Rails API:
https://github.com/rails-api/rails-api
WARNING: Can't verify CSRF token authenticity
Well one of my coworkers figured it out. The problem was, the has I was sending didn't have the same structure as the hash I was expecting in my controller.
In my controller I instantiate a new API connection as follows,
AustraliaPostApiConnection.new(params[:australia_post_api_connection])
I am looking for params[:australia_post_api_connection]
, but there is no such index in my data hash, which looks like,
var dataHash = {
to_postcode: to_postcode,
authenticity_token: tokentag // included based on an SO answer
}
To fix this I changed the JS file to contain,
var dataHash = {
to_postcode: to_postcode,
}
var params = {
australia_post_api_connection: dataHash,
authenticity_token: tokentag // included based on an SO answer
}
And now it works! Thanks co-worker!
Related Topics
How to Make Part of a Regular Expression Optional in Ruby
Split String into a List, But Keeping the Split Pattern
Forking a Gem for a Rails Project
How to Check for File Existence
Parsing Xls and Xlsx (Ms Excel) Files with Ruby
Ruby on Rails Rmagick on Windows 7
Why Does Code Need to Be Reloaded in Rails 3
What's the Point of Argv in Ruby
Why Doesn't Minitest::Spec Have a Wont_Raise Assertion
Get Index of String Scan Results in Ruby
How to Sort a Ruby Hash by Number Value
How to Get Sinatra to Auto-Reload the File After Each Change
Using a String as a Variable at Run Time
Rails Activesupport Time Parsing
How to Enable Cors in Rails 4 App
Running into Smtp Error When Trying to Send Email in Ror App