CSRF Token in Django and iOS - ios

So I am trying to understand what to do here... I am doing a POST call to my Django server from iOS and I keep getting the 403 Error (Invalid CSRF Token). I am thinking about implementing a function that will return me the token (you will need to be logged in to access that function), and then add the token to my POST call.
Now... I don't understand what is the point of doing that? If I use TastyPie and the required login is APIKey... should I just exempt the csrf check?
To make sure I understand things right... is the CSRF generated per user session? Therefore, if I don't use Cookies, CSRF is not necessary?
How do people usually use their Django Servers with an iOS and making such POST calls?
Thanks!

You're right: if you don't use cookies to manage your sessions, you don't need CSRF protection. CSRF works because session cookies are automatically attached to the request; access tokens are not.
I personally found this article very useful. It is definitely worth reading, and would probably answer a lot of your questions.
As for tastypie: it allows SessionAuthentication. If you allow session authentication in tastypie, I suggest you look into a way to protect your users against CSRF. For other authentication schemes this doesn't seem necessary. As far as I know, Dmitry is right about tastypie disabling CSRF by default, which means it is strange that you get that 403 Error. Perhaps there is something else going on. Try wrapping the view in #csrf_exempt.
As for CSRF tokens, they are also called session independent nonces. They are meant to be permanent, but you probably know that is impossible for cookies. Anyway, this means that CSRF cookies persist through sessions.

You're right, CSRF does not make much sense in this case, because its purpose is to protect users from data tampering in a browser.
I believe that Tastypie disables CSRF on its views by default.

Related

CSRF protection and Spring Session header session strategy

In my spring based rest API, I'm using spring-session with HeaderHttpSessionStrategy. Given cookies are not used at all (session id is sent as a header), do I still need to worry about CSRF attacks?
I would say I'm safe, and I have read people agreeing that in this scenario CSRF protection is not needed, for example:
https://security.stackexchange.com/questions/62080/is-csrf-possible-if-i-dont-even-use-cookies
However, the Spring guys argue that whenever an application is accessed by a browser you do need CSRF protection: https://spring.io/blog/2015/01/12/the-login-page-angular-js-and-spring-security-part-ii.
In my oppinion, if you use the HeaderHttpSessionStrategy, CSRF attacks are not possible. If you save the header token as a cookie, it is actually the same approach as the XSRF protection works. So XSRF protection does not give you any additional protection.
I'd stick with the Spring guys. Generally they know what they are talking about. CSRF attacks consist of malicious code running while your request is in transit, pretending that the request comes from you.
Enabling CSRF doesn't do any harm other than adding an hidden field to your form and it protects your web application against such attacks. Why not use it then?

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.

Rails API design without disabling CSRF protection

Back in February 2011, Rails was changed to require the CSRF token for all non-GET requests, even those for an API endpoint. I understand the explanation for why this is an important change for browser requests, but that blog post does not offer any advice for how an API should handle the change.
I am not interested in disabling CSRF protection for certain actions.
How are APIs supposed to deal with this change? Is the expectation that an API client makes a GET request to the API to get a CSRF token, then includes that token in every request during that session?
It appears that the token does not change from one POST to another. Is it safe to assume that the token will not change for the duration of the session?
I don't relish the extra error handling when the session expires, but I suppose it is better than having to GET a token before every POST/PUT/DELETE request.
Old question but security is important enough that I feel it deserves a complete answer. As discussed in this question there are still some risk of CSRF even with APIs. Yes browsers are supposed to guard against this by default, but as you don't have complete control of the browser and plugins the user has installed, it's should still be considered a best practice to protect against CSRF in your API.
The way I've seen it done sometimes is to parse the CSRF meta tag from the HTML page itself. I don't really like this though as it doesn't fit well with the way a lot of single page + API apps work today and I feel the CSRF token should be sent in every request regardless of whether it's HTML, JSON or XML.
So I'd suggest instead passing a CSRF token as a cookie or header value via an after filter for all requests. The API can simply re-submit that back as a header value of X-CSRF-Token which Rails already checks.
This is how I did it with AngularJS:
# In my ApplicationController
after_filter :set_csrf_cookie
def set_csrf_cookie
if protect_against_forgery?
cookies['XSRF-TOKEN'] = form_authenticity_token
end
end
AngularJS automatically looks for a cookie named XSRF-TOKEN but feel free to name it anything you want for your purposes. Then when you submit a POST/PUT/DELETE you should to set the header property X-CSRF-Token which Rails automatically looks for.
Unfortunately, AngualrJS already sends back the XSRF-TOKEN cookie in a header value of X-XSRF-TOKEN. It's easy to override Rails' default behaviour to accomodate this in ApplicationController like this:
protected
def verified_request?
super || form_authenticity_token == request.headers['X-XSRF-TOKEN']
end
For Rails 4.2 there is a built in helper now for validating CSRF that should be used.
protected
def verified_request?
super || valid_authenticity_token?(session, request.headers['X-XSRF-TOKEN'])
end
I hope that's helpful.
EDIT: In a discussion on this for a Rails pull-request I submitted it came out that passing the CSRF token through the API for login is a particularly bad practice (e.g., someone could create third-party login for your site that uses user credentials instead of tokens). So cavet emptor. It's up to you to decide how concerned you are about that for your application. In this case you could still use the above approach but only send back the CSRF cookie to a browser that already has an authenticated session and not for every request. This will prevent submitting a valid login without using the CSRF meta tag.
Rails works with the 'secure by default' convention. Cross-Site or Cross-Session Request Forgery requires a user to have a browser and another trusted website. This is not relevant for APIs, since they don't run in the browser and don't maintain any session. Therefore, you should disable CSRF for APIs.
Of course, you should protect your API by requiring HTTP Authentication or a custom implemented API token or OAuth solution.

CSRF protection and cross site form access

I aw working on cross site authentication (some domains have got common authentication). So I want to send authentication data (login, password) to main domain from others.
How should I use protect_from_forgery and how can I check if data received from valid domain?
What I am thinking now is to turn off protect_from_forgery for session controller and check domain name of received data.
But maybe I can configure CSRF protection for not only one domain?
The protection work with checking the session[:_csrf_token], so if you session is same in all of your domain, the protect_from_forgery can works.
What you are purposing is the very definition of a CSRF vulnerability. Forcing the user to Login or log-off generally isn't useful to the attacker. To pull off this hack the attacker would have to know the Username and Password, which defeats the purpose of "Session riding" on another user's authenticated session. As a hardcore hacker that writes CSRF exploits I'm telling you that this isn't a serious problem.
A easy way to patch this is to check the referer and make sure the login request is originating from a whitelist of domains. Another way to patch this is to use a Capthca like reCapthca. Yes you read that right. The reason why this works is because an attacker cannot solve the capthca with javascript or flash to "forge" a valid login request.

Rails, OAuth, and CSRF protection

I am using REST and OAuth to talk to a Rails app (from an iPhone app, but that should not be relevant). However, I am running into some issues with Rails' CSRF protection (via protects_from_forgery).
I understand that CSRF protection only kicks in for regular form submissions (i.e. Content-Type=application/x-www-form-urlencoded), so I would be fine if I was submitting JSON or XML data. Unfortunately, OAuth is currently limited to application/x-www-form-urlencoded requests. There's a draft spec that extends OAuth to non-form-urlencoded data, but this doesn't help me right now.
The way I see it, I have the following options:
Send the data as JSON, knowing that it would not be part of the OAuth signature and thus subject to man-in-the-middle attacks. Obviously not an attractive solution.
Create special Rails actions (e.g. UsersController#update_oauth) that internally delegate to the regular actions (e.g. UsersController#update). Then exclude these from the forgery protection (protects_from_forgery :only => [:update]). This should work and might be borderline acceptable for one or two actions, but obviously would be a very messy solution.
Override the Rails CSRF protection to ignore OAuth requests. I have not tried this, but it seems like it should be possible to change one of the hooks (perhaps the verify_authenticity_token filter) to consider OAuth requests successful.
Has anybody run into this before? Any recommendations? Or am I perhaps missing something basic?
I'll answer my own question. :)
I added the following method to our OAuth controller extensions. The only thing this adds on top of the default implementation is the oauth? check. This seems to do the trick and feels like a pretty clean solution.
def verify_authenticity_token
verified_request? || oauth? || raise(ActionController::InvalidAuthenticityToken)
end

Resources