Anyone had experience exposing a rails API while leaving the csrf token protection in place?

946 views
Skip to first unread message

2potatocakes

unread,
Jan 4, 2012, 4:57:50 PM1/4/12
to Ruby or Rails Oceania
Hi guys,

I haven't actually looked around properly yet (am doing it now..) but
thought I'd flick this question over here first anyways and see what
you all thought?

I'm upgrading an old behemoth of an application to 2.3.14 at the mo
and needed to add in CSRF protection for the entire site. Thing thing
is, the majority of the site is web based but about 40% of it also
acts as an API returning XML. I've updated my non get/post requests to
use the token and the site is working fine via the web but if I try
and access it via the existing API, my session data is now destroyed
as the API request does not include the token..

Anyone got any ideas on the best way to approach this? So far I've
only read people saying to turn it off for the particular methods you
want to expose.. which doesn't sound right to me..

Cheers if you can help.

Kind Regards,

Lucas

Pat Allan

unread,
Jan 4, 2012, 7:03:46 PM1/4/12
to rails-...@googlegroups.com

Hi Lucas

My approach has always been to turn of CSRF for API routes - if you don't want people being able to POST/PUT/DELETE, then authentication would be a good idea. Why does turning CSRF off for these routes not sound right to you?

--
Pat

> --
> You received this message because you are subscribed to the Google Groups "Ruby or Rails Oceania" group.
> To post to this group, send email to rails-...@googlegroups.com.
> To unsubscribe from this group, send email to rails-oceani...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/rails-oceania?hl=en.
>

ben wiseley

unread,
Jan 4, 2012, 7:38:57 PM1/4/12
to rails-...@googlegroups.com
fwiw I ran into this the other night as well, wasted a bunch of time on the googles and just ended up turning off csrf for the api and doing token auth for the api

2potatocakes

unread,
Jan 4, 2012, 7:57:24 PM1/4/12
to Ruby or Rails Oceania

Thanks Pat,

Every request within the site has to be done by an authenticated user
anyways so the sites partially secure as is. But CSRF attacks work by
actually using the users logged in session details, so normal
authentication doesn't protect it.

What I'm thinking is that it would at least be a bit safer if I could
somehow leave the CSRF protection on for all non XML requests.. This
would still leave the method open to attack but would at least reduce
the probability of it happening.. am looking into this now.

Kind Regards,

Lucas

Pat Allan

unread,
Jan 4, 2012, 8:08:56 PM1/4/12
to rails-...@googlegroups.com

My normal approach for APIs these days is this:
* API under a separate namespace - whether that's a subdomain (api.foo.com) or directory (foo.com/api) - and everything under that namespace has CSRF turned off. Keep your HTML/user focused controllers separate from your API controllers.
* Authentication and versioning by headers:
http://freelancing-gods.com/posts/versioning_your_ap_is
* More and more, I'm using Grape for my APIs, which means it's a Rack app mounted in my Rails app - and it supports versioning via headers out of the box (use git, not the latest gem, as that gem is very old):
https://github.com/intridea/grape
* Authentication is done via an api key, not the user's password - I certainly don't like hard-coding my passwords in code, but api keys can be regenerated (or even allow for many for any given user, and they can be added/removed as desired), which opens up options to allow third parties to use your API on behalf of a user, with the option of that user disabling that api key.

--
Pat

Adam Boas

unread,
Jan 4, 2012, 8:19:07 PM1/4/12
to rails-...@googlegroups.com
I've always found the CSRF token to be a really bad fit for API based interaction. It requires that you do a get before any post or put to get a fresh token. I have found it easier to secure your APIs using basic auth and pass your credentials on every request.

Cheers,

Adam 



On 05/01/2012, at 11:57 AM, 2potatocakes wrote:

Ivan Vanderbyl

unread,
Jan 4, 2012, 8:28:00 PM1/4/12
to rails-...@googlegroups.com
Hi Lucas,

CSRF protection is not required for API calls because you will likely never consume it using a browser, and more over never create a session, each API call should reauthenticate from a token or header etc. before processing anything.

Regards,
Ivan Vanderbyl

Sent from my iPhone

Paul Annesley

unread,
Jan 4, 2012, 8:47:23 PM1/4/12
to rails-...@googlegroups.com
CSRF protection should be considered a dependency of session-based authentication:

If you're using session-based authentication, you should always enable CSRF protection.

If you disable CSRF protection, you should also disable session-based authentication.

-- Paul

2potatocakes

unread,
Jan 4, 2012, 8:48:39 PM1/4/12
to Ruby or Rails Oceania

:) Thanks again, was also looking at alternate api authentication
methods so I'll have a look at what you've suggested thanks!

At the moment it looks like it'd be WAY too much work to try
separating out all this stuff and I'll never get approval to do it, I
agree with having separate namespaces though, it would make things a
lot cleaner but it is the way it is at the moment I'm afraid,
everything currently shares controller methods and respond_to blocks.

I've just added this to my application controller and it seems to be
working ok for what I need now though.

before_filter(:except => [:index, :show]) do |controller|
protect_from_forgery unless controller.request.format.xml?
end

We had a security profile of the site done recently and it scored
really well except for this CSRF vulnerability (apparently).. CSRF
attacks are so incredibly rare but this web security company still
made it sound like the sky was falling.. so hopefully this fix will
allow my managers to sleep at night now ;)

Thanks again for your input guys

Lucas

Paul Annesley

unread,
Jan 4, 2012, 9:03:02 PM1/4/12
to rails-...@googlegroups.com

On 05/01/2012, at 12:48 PM, 2potatocakes wrote:

> I've just added this to my application controller and it seems to be
> working ok for what I need now though.
>
> before_filter(:except => [:index, :show]) do |controller|
> protect_from_forgery unless controller.request.format.xml?
> end

This could leave you open to CSRF attacks that have ".xml" appended to the target URL.
It's certainly a big improvement on no protection at all, though.

-- Paul

Ivan Vanderbyl

unread,
Jan 4, 2012, 9:46:48 PM1/4/12
to rails-...@googlegroups.com
You're better of disabling forgery protection in the presence of some form of valid token authentication, not by method or request format.

So if the request is to be authenticated with a token/some form of API authentication, disable session and disable forgery protection, otherwise require it.

— Ivan

2potatocakes

unread,
Jan 4, 2012, 10:48:46 PM1/4/12
to Ruby or Rails Oceania
:) Gives me plenty to go back to my managers with.

What I suggested above was a quick fix and I now feel dirty for
posting it now.. The existing API looks like it has always just used
session based authentication.

The project I'm on has been under full time development since march
2006 and I'm not kidding when I say it's a behemoth.. it needs major
refactoring and has had stuff patched on top of it all over the place,
but the company already has quite a few customers that have already
built their own solutions on top of the existing API so API changes
have to be kept to an absolute minimum if any at all.

From what you've all said above, to do it properly looks like we'd
need to bite the bullet and move all api calls into a separately
namespaced area (not totally necessary, but desirable) and use token
authentication on every API request rather than session based, and
leave the CSRF protection on for everything else within the normal
website that uses the regular session based authentication.

This makes sense, was trying to find a solution that wouldn't impact
the existing customers too much but I don't think that's going to be
possible if they want this done properly. Will go and find out.

Thanks again for your help guys, much appreciate the input!

Lucas

Pat Allan

unread,
Jan 4, 2012, 10:52:43 PM1/4/12
to rails-...@googlegroups.com

One other thing you may want to consider is using rack-rewrite or similar to map .xml responses to a namespaced API - which allows existing clients to use the routes they're familiar with, but the new API setup (namespaced controllers, Grape, whatever) to handle all responses. Don't forget to write tests to ensure old routes behave as they always have, though :)

--
Pat

Leonard

unread,
Jan 4, 2012, 11:12:42 PM1/4/12
to rails-...@googlegroups.com
The best answer is that you need to create an entirely new API and slowly phase out the existing API as you move existing customers off it. If you're fairly happy with your existing API except for the authentication then the actual coding shouldn't take too long, but you'll probably need to spend a fair amount of time supporting clients who are switching to the new system.

Best of Luck!

-- Len
Reply all
Reply to author
Forward
0 new messages