Vincent Zhao
Vincent Zhao

Reputation: 167

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:

  1. How to do authentication checking for REST API server if it has :null_session specified?
  2. 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?
  3. 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.

Upvotes: 2

Views: 327

Answers (1)

EugZol
EugZol

Reputation: 6555

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

Upvotes: 1

Related Questions