Making a private REST API in Rails - ruby-on-rails

I want to make a small API in Rails, so that when it gets a POST request the data sent is stored in database, when it gets a GET request it returns the data...so on. The problem that I'm facing is the authenticity_token. When I try to make a POST request I get Can't verify CSRF token authenticity. I don't want to disable the authenticity_token because I would like to use it as a key for being able to use the API. Is it possible to make it static? If not what is the proper way to create a private REST API in Rails.

You can add at the top of the controller hit by the API call : skip_before_filter :verify_authenticity_token

Related

Invalid authenticity token when POSTing to a Rails API

I have a web application that gets & stores some data through a Rails API.
When I try to "post" to the Rails API, I get an error ActionController::InvalidAuthenticityToken. I know one option is to simply disable the authenticity token requirement on my Rails controller.
Is it possible to have my web app providing a correct authenticity token when it calls the Rails API? How can I do this?
2 Part answer for you here.
First if you are going to be using Rails as an API I would recommend you use another way of validating that the user making the request is actually the user they say they are such as creating a unique token upon account creation or login that can be returned in the initial response and provided as a HTTP header in subsequent requests. If you are worried about the security of this you could optionally base64 encode the key plus some other value and decode it server side before comparison.
If you still wish to use the CSRF method baked in to Rails you can do so as long as the user is making a request from the webapp using AJAX or whatever. If you have the csrf_meta_tags ERB in the header of your layout file you can get the value and set it in the X-CSRF-Token HTTP header. Using jQuery it may look something like:
$.ajaxPrefilter(function(options, originalOptions, xhr) {
var token = $('meta[name="csrf-token"]').attr('content');
if (token) xhr.setRequestHeader('X-CSRF-Token', token);
});
This would cause it to be added to every ajax request made using jQuery.

Rails REST API Server: protect_from_forgery option and where to put authentication token

update (14/08/2015): Thanks to #EugZol's idea, I've updated the title, as the previous one is misleading.
I'm trying to build a Rails REST API server now.
I've read others' posts that I could use protect_from_forgery with: :null_session to deal with the CSRF issue on JSON request, especially the POST request. I think this null_session will just clean the session(which is useless for JSON request).
But how could I do authentication on whether a user is logged in or not? I don't want to send password in every request. So I think maybe :null_session is not safe.
I'll be really appreciated if you could provide me some idea about:
How to do authentication checking for REST API server if it has :null_session specified?
If the solution is by using authenticate_token, where should I put this token in my JSON response? And where should the client put it?
Follow the previous one: Do I need to save a digest of that token in the database? I think the session will not be used now...
Thanks a lot.
If you don't use cookies to authorize your client, CSRF can't be accomplished. It works only because browser would automatically add cookie to the request. E.g. when you open the following picture:
<img src="my.bank/withdraw_funds?to=john_doe">
you browser will make request to my.bank with cookies attached, authorizing the transfer.
But if you use some API token (as a request parameter or as a header), there is no way to make a malicious image/form that will add that token to the request (because the attacker doesn't know a token).
And, yes, you should put this token into the database. Rails' session use cookies, and API server, as mentioned, shouldn't rely on them.
You authentication method (probably somewhere in ApplicationController) would look like this:
before_action :authenticate
def authenticate
#user = User.where(token: params.require(:token)).first
end
And then you can use #user in your actions. On the client side you just add ?token=... to your request parameters.
If you use headers (and it's better, at least because you shouldn't worry about token being saved in proxy servers' logs when you do GET requests), you add X-Authentication-Token: ... header to your request, and then modify your ApplicationController like that:
before_action :authenticate
def authenticate
#user = User.where(token: request.headers['HTTP_X_AUTHENTICATION_TOKEN']).first
end

Get around CSFR token for iOS app

I am developing an iOS app for a RoR api (my co-worker made it). I am trying to develop the login portion, but while testing the api in POSTMan, I noticed it requires a CSRF token. Is there a way to get around doing an api call to get the CSRF?
Side note: I am using AFNetworking 2.0
There are a couple things you can do:
You can launch a GET request before you do the post, and retrieve the sessions CSRF token. Then submit the POST form with an authenticity_token parameter as the proper CSRF token. You can embed the original token anywhere in the view with the rails helper form_authenticity_token, or just get it from the sign up form's hidden tag. (This is my favorite option)
You can make a secondary loggin-in action on your site that is actually a GET request in and of itself. It's not too dangerous to bypass the CRSF token here because anyone should have access to log in. This has the advantage of keeping CRSF for any other action you may need, but it wouldn't work for actions that need more security.
You can make your iOS page consist of a UIWebView. I'm not sure if this will suit your needs, but it would have the proper CSRF token and you can remove the UIWebView after submitting. It's kind of like option 1, but bulkier.
Good luck!
Easiest fix is to change the server side to not authenticate the CSRF token. Here's an example of using a different controller for your API.
class Api::BaseController < ApplicationController
skip_before_filter :verify_authenticity_token
end
In general, your API is either going to require authentication for API calls (in which case you should have your own authentication, or OAuth, or any number of authentication mechanisms) or isn't (in which case it's a publicly accessible API and CSRF doesn't matter). There a few other threads here and here that discuss it.
From another answer on SO (go upvote it!):
CSRF attacks rely on cookies being implicitly sent with all requests to a particular domain. If your API endpoints do not allow cookie-based authentication, you should be good.

Chrome extension: Send AJAX form to Rails app

I'm trying to make a Chrome extension for my Rails App that sends POST data with an ajax form. But, I get the response from the server:
ActionController::InvalidAuthenticityToken in AppController#getpostdata.
So I think I need to get an authenticity token and include that in my form.
Or, should I turn it off? And how?
Thanks
You can retrieve the token using the form_authenticity_token helper in a Rails view, during a GET request..
Alternately you can disable the token, or alternately use the :null_session option as this is considered the best for APIs. Consult the documentation for further info.
You can also skip the particular api method call in the controller as given below:
skip_before_filter :verify_authenticity_token, :only =>[:method_name]

Setting HTTP Headers for Rails form_for

I am currently working on an avatar app powered by Rails where users can upload avatars for their user profile.
I would like to use a custom HTTP header to block public upload requests and only allow requests from my apps. How would I go about doing this with Ruby on Rails?
I am uploading the avatars using AJAX so this may be a bit harder. Also I would prefer not to show the header in the public HTML code otherwise it defeats the object of adding it!
If you add
protect_from_forgery
to your application controller, it will block all NON Get requests from 3rd party links. It will add a hidden input value to each form with an authentication token that will be used to check all data that is sent to the servers.
Further reading
http://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf
http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html
Rails 3.1 - CSRF ignored?
You could implement a custom HTTP header (say X-Foobar-Validity-Status: valid) and check it in a before_filter.
class YourController < ApplicationController
before_filter :check_header
def check_header
unless request.headers['X-Foobar-Validity-Status'] == "valid"
render json: {"error" => "You are an evil attacker. Go away"}
end
end
end
However, I would consider this a bad idea.
Attackers can read the packet dump of your HTTP requests and add the headers, even with jQuery. See the jQuery.ajax headers option.
Instead of using a proprietary header, I would use User-Agent for this purpose.
Instead, I would sugest using the protect_from_forgery mechanism of rails. It makes your life easier and is more secure. Just fetch the authenticy token by a http request in your app and then send it back with your request. This should keep intruders out.

Resources