Drupal services/cors api not accepting CSRF token - ajax

I am using polymer to send ajax requests to my Drupal services api.
I send a POST to login and then a POST to create a node. When I login I am given a token which I store and pass to the next request.
I am monitoring the the requests and responses with Charles, the token is being sent, the cookie is being set and passed on the 2nd POST but I get an "Unauthorized : CSRF validation failed" response.
When I send the request with Postman It works like a dream but for some reason it doesn't validate when sent with my app.
I have checked the token being set matches the one being sent and the only difference I've noticed is that when it's being sent again there is a prefix of ga_; something to do with google analytics?
The expiry of the token is a month away the token matches what is returned at login and is being sent correctly. The header accepts X-CSRF-Token in the Access-Control-Allow-Headers.
My CORS module code is:
api/*|<mirror>|GET, POST, PUT, OPTIONS|Authorization, Origin, Content-Type, X-CSRF-Token|true

If any body has a similar issue, mine was caused by a couple of things, running Drupal and my app in the same browser causing all kinds or cookie conflicts and when passing parameters to my function that computes my request, if there were any parameters that were not used it breaks.
Hope this helps someone.

Related

Ajax request to Django only succeeds if there is no sessionid cookie

I have sessions enabled in Django to use Django's authentication framework.
From a html page served by Django, and after authenticating as a user with sufficient permissions, I'm trying to send a PATCH request via JQuery's ajax() function, and I'm getting HTTP 403 errors with the response detail CSRF Failed: CSRF token missing or incorrect.
What I've done so far:
I'm including the correct csrf token in the X-CSRF-TOKEN header field.
I've set SESSION_COOKIE_HTTPONLY = False.
The cookie sent in the ajax request includes the sessionid. If I get rid of this sessionid, the request succeeds.To do so, I either delete the session cookies in the browser or edit the PATCH request in the browser's developer tools and resend it with the sessionid deleted from the Cookie header field. Obviously I need to re-login as soon as I refresh the page, but in the meantime, I can PATCH to my heart's content.
So far I couldn't find out why the presence of the sessionid cookie makes Django deny the request.

When does Spring send back a XSRF-TOKEN set-cookie response header?

The title sums it all up nicely. I'm asking for the conditions that should be met so Spring decides to send back a Set-Cookie:XSRF-TOKEN=... response header.
I can see that lots of my requests are getting back responses with such header while it is not needed. For instance, when I send a GET request it gets a response with that header set even though I had set the X-XSRF-TOKEN for the request. But for the POST request having the mentioned request header will stop Spring from sending back the set-cookie header. So I wonder what are the conditions that should be met so Spring decides to send back one.
I spent some time tracing Spring Security's source code and I managed to find out the answer myself.
First of all, I didn't know how CSRF works and tracing the code helped me understand it completely. And I think this is something worth knowing. Here's the scenario of requests and responses sent and received from CSRF point of view:
The first request is sent to the server. Since it's the first one, it has no cookie or header set.
Regardless of the request's method, the server (in here Spring Security) looks into the incoming request. If there's no cookie sent to the server under the name XSRF-TOKEN, it will generate a SET COOKIE header on the response's way back.
The client receives the cookie and for the second request, it will add a header to the request under the name X-XSRF-TOKEN set to the same value as the cookie received. Of course, the cookie will be sent to the server with the second request automatically.
When the server receives the second request, this time it has a XSRF-TOKEN cookie, so it will look for a X-XSRF-TOKEN header and the request is considered valid only if these two strings match.
And as for the answer to my question, as I mentioned in step two, the cookie is generated if there's no cookie sent (with XSRF-TOKEN name) to the server. And it does not rely on any other factor - what so ever!

Heroku CSRF and POST httpRequest

I have a web crawler on Heroku and I'm trying to call the script from a POST request on Parse Cloud Code httpRequest but I receive a 403 forbidden response basically telling me the Referer Header didn't pass. How can I get past this?
Django's CSRF protection tests the Referer header: see https://docs.djangoproject.com/es/1.9/ref/csrf/#how-it-works. Browsers typically send that header to indicate the page that originated a request, but programmatic user agents don't (cURL, Python requests, and presumably Parse.Cloud.httpRequest) without being told to do so.
To add custom headers to a Parse request, see: Parse.Cloud.httpRequest call with HTTP request header (note the headers object).
That said, you also need to make sure you have a way to get the CSRF token to begin with, and include it either in a XCSRF-Token header or a form field (unclear from your question whether you are doing that).

How to handle CSRF in preflighted CORS POST request in django?

I am trying to make POST request via AJAX from abc.com to URL from xyz.com (which is a Django application).
I am getting CSRF token by making a GET request to a URL on xyz.com, but the token changes when an OPTIONS request is made to xyz.com in the preflighted request.
Is there any way to get the response of OPTIONS request in the preflighted request ?
Note:
I am following instructions from following sources :
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
https://developer.mozilla.org/en/docs/HTTP/Access_control_CORS
http://www.html5rocks.com/en/tutorials/cors/
Django CSRF protection will allow OPTIONS requests, so no problem with the first stage:
https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#how-it-works
If I understand correctly, you then want the next request (e.g. a cross-domain POST) to be allowed through. For this to work and get past Django's CSRF protection, the request must send a CSRF token (in POST data or in header for AJAX) and a matching CSRF cookie.
Now, cross-domain restrictions make it impossible for abc.com to set or read a cookie for xyz.com, whether from javascript or from a server side response. Therefore, this approach is impossible.
Instead you will have to apply #csrf_exempt to the view. This would allow any site to post to it. Therefore, you'll need to build in some other protection to the view. You are, of course, on your own in checking the security of your protection. Remember that 'Referer' and 'Origin' headers can easily be forged with something as basic as curl.
See django-cors-headers, you may find it how it works more suitable to solve your problem:
https://github.com/ottoyiu/django-cors-headers/
Django-rest-framework recommends http://www.django-rest-framework.org/topics/ajax-csrf-cors

Send credentials in preflighted request (CORS)

I have to make CORS with content type JSON. This makes the browser send first an OPTIONS request, specifying origin and so on. Then the server should answer with allowed domains, methods, etc. If this goes well, the browser sends the actual request.
My problem is that the server needs authentication for the actual request but ALSO for the OPTIONS request. But the browser doesn't send the authentication headers with the OPTIONS request.
I'm using JQuery and the ajax() function. I tried adding "withCredentials: true", and add the Authorization header, but this not affect the OPTIONS request, it still doesn't send any credentials.
Any ideas? Thanks in advance.
The preflight request is only meant to verify if the CORS request itself is allowed. Therefore, cookies are never included in the preflight request. You'd have to validate the user during the actual request.
Actually, Chrome will send the cookies w/ the preflight request if withCredentials=true whereas Firefox will not. Sounds like they've implemented the spec differently.

Resources