I have an existing rails backend website which makes json calls to server. Now,I am developing a mobile iOS app to use the same backend and send calls in json. However, mobile requests are failing with:
WARNING: Can't verify CSRF token authenticity
Searching around stackoverflow, many suggested to disable csrf checks for json calls by using something like this:
# Or this in your application_controller.rb
def verified_request?
if request.content_type == "application/json"
true
else
super()
end
end
But my question is , I dont understand how does this prevent csrf attacks in json format? Attacker can always send a json request to our endpoint from their site. Anyone has insights into this? I couldn't find any clear answer to this.
What you are describing is very easy to exploit using Flash:
var request:URLRequest = new URLRequest("http://stackoverflow.com");
request.requestHeaders.push(new URLRequestHeader('Content-Type', 'application/json'));
request.data = unescape('{"a":1,"b":{"c":3}}');
request.method = URLRequestMethod.POST;
navigateToURL(request, '_blank');
If you look at the CSRF prevention cheat sheet you can check the referer to make sure its from a domain you trust. If the referer is blank then it could be originating from a https url, so that should be considered a failure. Relying on Ruby's CSRF token is a stronger form a CSRF protection.
This is a fix for ajax
Get csrf_token from rails or if using something else, from meta
// js file
var csrf_token = $('meta[name=csrf-token]').attr('content');
or
//js.erb file
var csrf_token = "<%= request.session["<%= _csrf_token %>"] %>";
then add this to js
$("body").bind("ajaxSend", function(elm, xhr, s){
if (s.type == "POST") {
// place lines mentioned above here
// line goes here...
xhr.setRequestHeader('X-CSRF-Token', csrf_token);
}
});
One approach you can take is to leave on CSRF checks but disable based on a http header being present. If your json requests have a JWT token, you can do something like this in the relevant controllers.
protect_from_forgery with: :exception, unless: -> { some_auth_token.valid? }
Form submissions won't be able to set a http header and thus csrf will protect against it.
You json requests should have an Authorization
header to be secure.