When I make a post request to my node.js backend using axios and I look in my chrome console network tab, I see 2 http requests to the endpoint instead of 1.
The first one has a status code of 200 and a response of GET,HEAD,POST
The second one is the one I was expecting which is a status code of 200 and whatever I set my response to be.
Is it normal to get that 1st response of GET,HEAD,POST as well or am I doing something wrong here?
As you indicated that you're using different hosts (or ports at least), this is a default behaviour of browsers to check, if the CORS protocol is understood.
From MDN:
CORS (Cross-Origin Resource Sharing) is a system, consisting of transmitting HTTP headers, that determines whether browsers block frontend JavaScript code from accessing responses for cross-origin requests.
The same-origin security policy forbids cross-origin access to resources. But CORS gives web servers the ability to say they want to opt into allowing cross-origin access to their resources.
You can find more about CORS and prefligh requests in the MDN docs.
Related
So I'm trying to implement the OAuth 2 flow, while my webapp is the server that give away authorization code/access token.
Cors error happens when sending the code back to third-party website(zapier in this case):
XMLHttpRequest cannot load https://zapier.com/dashboard/auth/oauth/return/App505CLIAPI/?code=somecode&state=somestate. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://myurl' is therefore not allowed access.
If I manually open a new tab, paste that zapier uri, everything works perfectly.
Seems like a typical CORS issue, but none the popular solutions works for me:
Add Access-Control-Allow-Origin: I'm using this oauth2orize
library, and sending reponse to preflight seems also part of the
library. So I cannot add header.
Using cors: Have tried app.use(cors()) and app.options('*',
cors()) which should apply to all routes, but simply not working.
My webapp sits on a node express server, in front of which there's an nginx proxy server.
Any idea where the problem might be is appreciated.
The issue that error message indicates isn’t caused by the app code running at https://myurl/. Instead it’s just that https://zapier.com/dashboard/auth/… doesn’t seem to support CORS.
Specifically, the response from that https://zapier.com/dashboard/auth/… URL doesn’t include the Access-Control-Allow-Origin response header, so your browser won’t let your frontend JavaScript code access the response.
It seems like that is all intentional on the part of Zapier—they don’t intend for that auth endpoint to be accessed from frontend AJAX/XHR/Fetch code running in a browser. Instead I guess it’s intended that you only access that auth endpoint from your backend code. Or something.
Anyway there is no way from your side that you can fix the fact the response from that Zapier API endpoint doesn’t include Access-Control-Allow-Origin.
And as long as it doesn’t include Access-Control-Allow-Origin, your browser blocks your frontend code from being able to get to the response—and there’s no way to get your browser to behave otherwise as long as your frontend code is trying to hit that API endpoint directly.
So the only solution is to not hit that API endpoint directly from your frontend code but to instead either set up a proxy and change your frontend code to make the request through that, or else just handle it in some other way in your existing backend code, as mentioned above.
The answer at Why does my JavaScript code receive a "No 'Access-Control-Allow-Origin' header is present on the requested resource" error, while Postman does not? gives some details on how you can set up a special CORS proxy, if you want to go that route.
I'm a bit confused about the security aspects of CORS POST requests. I know there is a lot of information about this topic online, but I couldn't find a definite answer to my questions.
If I understood it correctly, the goal of the same-origin policy is to prevent CSRF attacks and the goal of CORS is to enable resource sharing if (and only if) the server agrees to share its data with applications hosted on other sites (origins).
HTTP specifies that POST requests are not 'safe', i.e. they might change the state of the server, e.g. by adding a new comment. When initiating a CORS request with the HTTP method POST, the browser only performs a 'safe' preflight request if the content-type of the request is non-standard (or if there are non-standard http headers). So POST requests with standard content-type and standard headers are executed and might have negative side effects on the server (although the response might not be accessible to the requesting script.)
There is this technique of adding a random token to every form, which the server then requires to be part of every non-'safe' request. If a script tries to forge a request, it either
does not have the random token and the server declines the request, or
it tries to access the form where the random token is defined. This response with the random token should have the appropriate head fields, such that the browser does not grant the evil script access to this response. Also in this case the attempt fails.
My conclusion is that the only protection against forged POST requests with standard content-type and headers is the technique described above (or a similar one). For any other non-'safe' request such as PUT or DELETE, or a POST with json-content, it is not necesssay to use the technique because CORS performs a 'safe' OPTIONS request.
Why did the authors of CORS exclude these POST exempt from preflight requests and therefore made it necessary to employ the technique described above?
See What is the motivation behind the introduction of preflight CORS requests?.
The reason CORS doesn’t require browsers to do a preflight for application/x-www-form-urlencoded, multipart/form-data, or text/plain content types is that if it did, that’d make CORS more restrictive than what browsers have already always allowed (and it’s not the intent of CORS to put new restrictions on what was already possible without CORS).
That is, with CORS, POST requests that you could do previously cross-origin are not preflighted—because browsers already allowed them before CORS existed, and servers knew about them. So CORS changes nothing about those “old” types of requests.
But prior to CORS, browsers wouldn’t allow you to do a cross-origin application/json POST at all, and so servers could assume they wouldn’t receive them. That’s why a CORS preflight is required for those types of “new” requests and not for the “old” ones—to give a heads-up to the server: this is a different “new” type of request that they must explicitly opt-in to supporting.
After reading a lot about CORS and pre-flight requests I still don't quite get why there are some exceptions for doing a pre-flight. Why does it matter if the Content-Type is text/plain or application/json?
If I get it right, the value of CORS is to restrict the returned data (It doesn't care if the POST destroyed the database, it only cares that the browser can't read the output of that operation). But if that's true (and probably It's not) why there are pre-flight requests at all? Wouldn't suffice to just check for a header like Access-Control-Allow-Cross-Origin-Request: true in the response?
The best answer so far I found in the: What is the motivation behind the introduction of preflight CORS requests? question, but it's still a bit confusing for me.
Why does it matter if the Content-Type is 'text/plain' or
'application/json'?
The three content types (enctype) supported by a form are as follows:
application/x-www-form-urlencoded
multipart/form-data
text/plain
If a form is received by a handler on the web server, and it is not one of the above content types then it can be assumed that it was an AJAX request that sent the form, and not an HTML <form /> tag.
Therefore, if an existing pre-CORS system is using the content type as a method of ensuring that the request is not cross-site in order to prevent Cross-Site Request Forgery (CSRF), then the authors of the CORS spec did not want to introduce any new security vulnerabilities into existing websites. They did this by insisting such requests initiate a preflight to ensure both the browser and the server are CORS compatible first.
It doesn't care if the POST destroyed the database, it only cares that
the browser can't read the output of that operation
Exactly right. By default browsers obey the Same Origin Policy. CORS relaxes this restriction, allowing another Origin to read responses from it made by AJAX.
why there are pre-flight requests at all?
As said, to ensure that both client and server are CORS compatible and it is not just an HTML form being sent that has always been able to be submitted cross domain.
e.g. this has always worked. A form on example.com POSTing to example.org:
<form method="post" action="//example.org/handler.php" />
Wouldn't suffice to just
check for a header like 'Access-Control-Allow-Cross-Origin-Request:
true' in the response?
Because of the CSRF vector. By checking that the browser can send a preflight, it ensures that the cross-origin request is authorised before the browser will send it (by examining CORS response headers). This enables the browser to protect the current user's session - remember that the attacker here is not the one running the browser, the victim is running the browser in a CSRF attack, therefore a manipulated browser that doesn't properly check CORS headers or spoofs a preflight would be of no advantage for an attacker to run themselves. Similarly, the preflight enables CSRF mitigations such as custom headers to work.
To summerise:
HTML form cross-origin
Can only be sent with certain enctype's
Cannot have custom headers
Browser will just send it without preflight because everything about a <form> submission will be standard (or "simple" as CORS puts it)
If server handler receives a request from such a form it will act upon it
AJAX cross-origin
Only possible via CORS
Early version of some browsers, like IE 8 & 9 could send cross-origin requests, but not with non-standard headers or enctype's
Can have custom headers and enctype's in fully supported browsers
In order to ensure that a cross-origin AJAX request is not spoofing a same-origin AJAX request (remember that cross-origin didn't used to be possible), if the AJAX request is not simple then the browser will send a preflight to ensure this is allowed
If server handler receives a request it will act upon it, but only if it has passed preflight checks because the initial request will be made with the OPTIONS verb and not until the browser agrees that the server is talking CORS will it send the actual GET or POST
Im trying to figure what is cors.
In MDN it describe as :
A resource makes a cross-origin HTTP request when it requests a resource from a different domain than the one which the first resource itself serves.
Im not sure I know what is a web resource.
In addition, I understand thats cors allows me to use web resource from another domain in my domain by putting the domain in the header, but is it just convention or something more than that?
Let me try to give a short explanation.
Web resource
A web resource is anything you request on the web. That could be an image, a json payload, a pdf, an html-page etc. There's not more to it than that.
CORS
When you want to do an ajax-request in a browser (typically from javascript), you are typically limited to making requests to resources (url's) on the same domain. Eg. www.x.com can only request resources from www.x.com. Let's imagine you have a web page on www.x.com that want's to get a resource from api.x.com. This will not be possible unless the server (api.x.com) has CORS enabled.
So how does it work? Well, the flow is like this (simplified a lot).
When you do a ajax-request, for instance a GET request for a json payload, the browser sees this and issues an OPTIONS request to server in which it states who it is (www.x.com in the Origin header). The server is then supposed to answer with a response with a header saying that it is ok for www.x.com to do the GET request. The server does this by adding a header Access-Control-Allow-Origin: www.x.com. If the allowed origin matches the origin in the request, the browser issues the GET request and the json payload is returned by the server. If the allowed origin does not match, the browser refuses to do the request and shows an error in the console.
If you are doing the client (www.x.com), and are using - lets say jquery - you don't have to do anything. Everything happens automatically.
If you are doing the server (api.x.com), you have to enabled CORS. How this is done varies a lot but http://enable-cors.org/server.html has a nice guide on how to do it on different server types. They also have some more in depth guides on how it works. Specifically you might wanna take a look here https://www.nczonline.net/blog/2010/05/25/cross-domain-ajax-with-cross-origin-resource-sharing/
I hope this helps you out a bit
I'm working on a chrome app and finally got to the point of issuing a PUT to the node.js server. My GET logic is working fine. My PUT however gets hijacked into a OPTIONS request. My requests are made to
http://localhost:4000/whatever
I read about the OPTIONS pass asking permission to do the PUT. I was under the impression that BROWSERS issue OPTIONS when CORS is requested, but didn't realize that a chrome app would also do this for me.
Is the app doing this because I didn't and I'm supposed to, or is this SOP that chrome will issue the OPTIONS request and I just issue my PUT that triggers it?
My PUT never makes it to the server. I've tried issuing my own OPTIONS just ahead of my PUT but so far nothing is working. The OPTIONS request makes it to the server (the default one or mine), but that's the end of the conversation.
At the server, all I'm doing to satisfy the OPTIONS request is as follows:
case 'OPTIONS':
res.writeHead(200, {'Access-Control-Allow-Methods': 'OPTIONS, TRACE, GET, HEAD, POST, PUT',
'Access-Control-Allow-Origin': "*"});
break;
When I try issuing my own OPTIONS & PUT requests, I'm doing them with separate XMLHttpRequest objects. I don't see where the permission hand off from OPTIONS to PUT is made.
This is called "preflighting", and browsers MUST preflight cross-origin requests if they fit specific criteria. For example, if the request method is anything other than GET or POST, the browser must preflight the request. You will need to handle these OPTIONS (preflight) requests properly in your server.
Presumably, your page is hosted on a port other than 4000, and the call to port 4000 is then considered cross-origin (in all browsers other than IE). Don't issue the OPTIONS request yourself. Chrome will then preflight your request. Your server must respond appropriately. The browser will handle the response to this OPTIONS request for you, and then send along the PUT as expected if the OPTIONS request was handled properly by your server.
There is an excellent article on Mozilla Developer Network that covers all things CORS. If you plan on working in any cross-origin environment, you should read this article. It will provide you with most of the knowledge necessary to understand the concepts required to properly deal with this type of an environment.