When doing CORS requests that require preflight, they fail to work on our network. Instead of Chrome making two requests, the OPTIONS and PUT, it fires two OPTIONS requests that are exactly the same, this ends up in the following error:
XMLHttpRequest cannot load http://server/api/foo. Response for preflight is invalid (redirect)
Digging into the problem, there are two things that stand out.
If I turn on Throttling in the Network section of the Chrome developer tools, things work and the OPTIONS request is followed by a PUT.
Under the timing tab, it almost looks like as if it tries to fire both (First request,
Second request) requests simultaneously. Despite the fact that a request is queued at a later time, the Started At time shows it at the same request as the initial OPTIONS request.
The throttled working, and non-working requests look fairly similar for comparison.
Related
I am looking at my ajax request in network tab in chrome and I noticed every ajax request I do happens twice.
First one is a 204 and then followed up with 200. My ajax call is only being hit once so I am not sure why there are 2.
Edit
So it seems to have to do with Cors, which I have just set to star (*) for testing.
I guess there is not to much I can do to not have it do 2 requests, but what really gets me is why it takes so long, I looking at google chrome network and on my page these 204 took anywhere from 110ms to 1.97 seconds.
That's a consequence of the CORS - Cross-origin resource sharing "protocol".
When doing requests do other domains, the browser do a request before your request, asking for the server if it can proceed with that request.
This request uses the method OPTIONS and should have no content, just response headers, that's why the response code is 204 (no content). After confirming that the request is allowed, the browser proceed with your request, that now will return 200 (or any other) status code.
When you try to send a AJAX request to a different domain, you are violating the same-origin policy.
Server that you are sending the request allows cross domain requests. In the process, there should be a preflight call and that is the HTTP OPTION call.
So, you are having two responses for the OPTION and your result.
I am newbie to website security and currently trying to understand Same-Origin-Policy in some depth.
While there are very good posts on stackoverflow and elsewhere about the concept of SOP, I could not find updated information on whether chrome and other browsers allow cross-domain XHR post requests to be 'sent' from the first place.
From this 5 year old post, it appears that chrome allows the request to pass through to the requested server but does not allow reading the response by the requester.
I tested that on my website trying to change user info on my server from a different domain. Details below:
My domain: "www.mysite.com"
Attacker domain: "www.attacker.mysite.com"
According to Same-Origin-Policy those two are considered different Origins.
User (while logged in to www.mysite.com) opens www.attacker.mysite.com and presses a button that fires a POST request to 'www.mysite.com' server...The submitted hidden form (without tokens in this case) has all the required information to change the user's info on 'www.mysite.com' server --> Result: CSRF successful attack: The user info does indeed change.
Now do the same but with javascript submitting the form through JQuery .post instead of submitting the form--> Result: Besides chrome giving the normal response:
No 'Access-Control-Allow-Origin' header is present on the requested
resource
, I found that no change is done on the server side...It seems that the request does not even pass through from the browser. The user info does not change at all! While that sounds good, I was expecting the opposite.
According to my understanding and the post linked above, for cross-domain requests, only the server response should be blocked by the browser not sending the post request to the server from the first place.
Also, I do not have any CORS configuration set; no Access-Control-Allow-Origin headers are sent. But even if I had that set, that should apply only on 'reading' the server response not actually sending the request...right?
I thought of preflights, where a request is sent to check if it's allowed on the server or not, and thus blocking the request before sending its actual data to change the user info. However, according to Access_Control_CORS , those preflights are only sent in specific situations which do not apply to my simple AJAX post request (which includes a simple form with enctype by default application/x-www-form-urlencoded and no custom headers are sent).
So is it that chrome has changed its security specs to prevent the post request to a cross domain from the first place?
or am I missing something here in my understanding to the same-origin-policy?
Either way, it would be helpful to know if there is a source for updated security measures implemented in different web browsers.
The XMLHttpRequest object behavior has been revisited with time.
The first AJAX request were unconstrained.
When SOP was introduced the XMLHttpRequest was updated to restrict every cross-origin request
If the origin of url is not same origin with the XMLHttpRequest origin the user agent should raise a SECURITY_ERR exception and terminate these steps.
From XMLHttpRequest Level 1, open method
The idea was that an AJAX request that couldn't read the response was useless and probably malicious, so they were forbidden.
So in general a cross-origin AJAX call would never make it to the server.
This API is now called XMLHttpRequest Level 1.
It turned out that SOP was in general too strict, before CORS was developed, Microsoft started to supply (and tried to standardize) a new XMLHttpRequest2 API that would allow only some specific requests, stripped by any cookie and most headers.
The standardization failed and was merged back into the XMLHttpRequest API after the advent of CORS. The behavior of Microsoft API was mostly retained but more complex (read: potentially dangerous) requests were allowed upon specific allowance from the server (through the use of pre-flights).
A POST request with non simple headers or Content-Type is considered complex, so it requires a pre-flight.
Pre-flights are done with the OPTIONS method and doesn't contain any form information, as such no updates on the server are done.
When the pre-flight fails, the user-agent (the browser) terminate the AJAX request, preserving the XMLHttpRequest Level 1 behavior.
So in short: For XMLHttpRequest the SOP was stronger, deny any cross-origin operations despite the goals stated by the SOP principles. This was possible because at the time that didn't break anything.
CORS loosened the policy allowing "non harmful" requests by default and allowing the negotiation of the others.
OK...I got it...It's neither a new policy in chrome nor missing something in SOP...
The session cookies of "www.mysite.com" were set to "HttpOnly" which means, as mentioned here, that they won't be sent along with AJAX requests, and thus the server won't change the user's details in point (4).
Once I added xhrFields: { withCredentials:true } to my post request, I was able to change the user's information in a cross-domain XHR POST call as expected.
Although this proves the already known fact that the browser actually sends the cross-domain post requests to the server and only blocks the server response, it might still be helpful to those trying to deepen their understanding to SOP and/or playing with CORS.
I've been working on a classic SPA where the front end app lives on app.example.com while the API lives on api.example.com, hence requiring the use of CORS requests. Have setup the server to return the CORS header, works fine.
Whenever an AJAX request is not simple, the browser makes an extra OPTIONS request to the server to determine if it can make the call with the payload. Find Simple Requests on MDN
The question is: What are the actual benefits of doing the OPTIONS request, especially in regards to security?
Some users of my app have significant geographical latency and since the preflight cache doesn't last long, the preflight requests cause latencies to be multiplied.
I'm hoping to make POST requests simple, but just embedding the Content-Type of application/json negates that. One potential solution is to "hack" it by using text/plain or encoding in the url. Hence, I hope to leave with a full understanding of what CORS preflight requests do for web security. Thanks.
As noted on the article you linked to:
These are the same kinds of cross-site requests that web content can
already issue, and no response data is released to the requester
unless the server sends an appropriate header. Therefore, sites that
prevent cross-site request forgery have nothing new to fear from HTTP
access control.
Basically it was done to make sure CORS does not introduce any extra means for cross-domain requests to be made that would otherwise be blocked without CORS.
For example, without CORS, the following form content types could only be done cross-domain via an actual <form> tag, and not by an AJAX request:
application/x-www-form-urlencoded
multipart/form-data
text/plain
Therefore any server receiving a request with one of the above content-types knows that there is a possibility of it coming from another domain and knows to take measures against attacks such as Cross Site Request Forgery. Other content types such as application/json could previously only be made from the same domain, therefore no extra protection was necessary.
Similarly requests with extra headers (e.g. X-Requested-With) would have previously been similarly protected as they could have only come from the same domain (a <form> tag cannot add extra headers, which was the only way previously to do a cross-domain POST). GET and POST are also the only methods supported by a form. HEAD is also listed here as it performs identically to GET, but without the message body being retrieved.
So, in a nutshell it will stop a "non simple" request from being made in the first place, without OPTIONS being invoked to ensure that both client and server are talking the CORS language. Remember that the Same Origin Policy only prevents reads from different origins, so the preflight mechanism is still needed to prevent writes from taking place - i.e. unsafe methods from being executed in a CSRF scenario.
You might be able to increase performance using the Access-Control-Max-Age header. Details here.
Via Firefox, if I do a GET text/html request to my web app, I get a 200 response back, and then Firefox sends 3 more of the same request right afterward. All return 200s. Does anyone know what would cause this?
*Some other observations about the issue:
In Firebug's network tab, only one request shows up. I can only see the extra requests using Tamper Data or another tool that sees the Http requests sent from my browser.
This issue does not happen in prior versions of my web app. When I compare the responses that get returned by the two different versions of the web app, I can't see anything that would cause this issue (but then, I really don't know what to look for). The responses are identical except for the web app's cookies, which are different.
This issue happens with JavaScript enabled or disabled.
Something similar is happening with Chrome, though it seems to be sending only 2 extra requests.
I don't see any browser redirects in the Html header.
This is only happening with text/html requests, not css requests, for example.
All 4 responses returned seem to have the complete Html page in the body, and they also have the cookie that the web app uses.
In Tamper Data, the 'Load Flags' column (whatever that is) says the following: First request is VALIDATE_ALWAYS_LOAD_DOCUMENT_URI LOAD_INITIAL_DOCUMENT_URI; second and third requests are LOAD_NORMAL; fourth request is LOAD_FROM_CACHE VALIDATE_NEVER
I don't see it happening with POSTs
It does not happen when the response is a 302.
If I go into the firefox config and set network.http.max-connections-per-server to 1, then Firefox only sends one request (the issue does not occur). (I don't think I can ask all our users to do that. :-))
*Why this issue is a problem:
This site has been around a long time and wasn't designed for this behavior. It's probably not going to go well.
(edited to add new findings)
I am trying to make cross-domain requests with Safari on Windows. My Safari version is 5.1.2.
This is a classical question. I read in many places that Chrome and Safari allows cross domain requests as long as Server responds with the followin header in the response
Access-Control-Allow-Origin: *
I have read this post.
How to allow cross-domain requests in Safari?
and many others on the stackoverflow site too.
However, none of them answers my question.
I am having problems with Chrome AND Safari doing cross-domain AJAX requests even though I am sending the necessary header back from the server.
I finally ran Chrome with "--disable-web-security". Then it worked.
My questions:
1) What do I do with Safari? Do I use a similar command line argument?
2) More importantly, can I someone please tell me whether cross-domain functionality is allowed in Chrome and Safari by default as long as server responds with the header or do I have to make sure that
a) server responds with a header
AND
b) browser is started with a proper argument.
I found the problem. Reading more about CORS helped html5rocks.com/en/tutorials/cors. I realized that my requests were triggering preflight requests (OPTIONS) and the server was not set up to handle these requests properly. The reason it was causing preflight requests was because I was using JQuery and it was adding a custom header into my requests. I modified my code to prevent addition of this extra header and my requests no longer needed preflight requests. Now I do not have to disable web security and it works fine.