NextJS cors has wrong 'Access-Control-Allow-Origin' value - next.js

I noticed an issue after deploying to Netlify, chrome was blocking my ajax requests to a public Apple api, but it was working for the friends I sent a link to.
Now it's broken in the opposite direction. It seems like the header is my netlify url rather than localhost in development.
Access to fetch at 'https://itunes.apple.com/search?entity=podcast&term=test&limit=6'
from origin
'http://localhost:3001' has been blocked by CORS policy: The 'Access-Control-Allow-Origin'
header has a value 'https://effulgent-dingus-192e6.netlify.app'
that is not equal to the supplied origin. Have the server send the header with a valid value,
or, if an opaque response serves your needs, set the request's mode to 'no-cors'
to fetch the resource with CORS disabled.
How can I make sure the Access-Control-Allow-Origin isn't cached (or fix whatever the problem is).

I found that NextJS was running the API call on the server side as well as the intended client side. I wrapped the call in a check to see if window !== undefined to do the call client side only. I think this has fixed it.

To prevent caching, include a Vary: Origin header in the response.
CORS and caching
Suppose the server sends a response with an Access-Control-Allow-Origin value with an explicit origin (rather than the "*" wildcard). In that case, the response should also include a Vary response header with the value Origin — to indicate to browsers that server responses can differ based on the value of the Origin request header.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin#cors_and_caching

Related

How does a server know a request is sent with credentials?

I've been having a problem that just cropped up when requesting a resource from another server through my website.
I request the resource (a PDF file requested through Range Requests).
The browser (Chrome in this case) sends an OPTIONS request to the server.
The server returns 200 from the OPTIONS, but one of the headers is - Access-Control-Allow-Credentials: true
Since the server exposes the wildcard * for the Access-Control-Allow-Origin response header, Chrome then (I think) throws out the following responses from the server and gives me an error saying that the server can't expose the wildcard if the request is made withCredentials = 'include'.
Now, I'm not setting the withCredentials flag anywhere in my code that I can see and I can't seem to find out how the server knows if a request is sent withCredentials. My cookies aren't sent with the OPTIONS or following GET/PARTIAL CONTENT requests. There's no other special headers in the request that I can see.
So,
How does the server know and how can I tell on the client side wither credentials are set to include?
If I'm not setting credentials on my side, what could be causing Chrome to think that the mode I'm using is withCredentials = 'include'?
Should the server be sending back the Access-Control-Allow-Credentials: true header on ALL requests after the OPTIONS?
How does the server know and how can I tell on the client side wither credentials are set to include?
The receiving server doesn’t know anything about the client-side withCredentials setting. The server just receives some form of credentials in the request, or doesn’t. And as far as the CORS protocol goes, the receiving server doesn’t change its behavior based on whether the request includes credentials or not. The receiving server either just sends back the Access-Control-Allow-Credentials: true response header, or doesn’t.
If I'm not setting credentials on my side, what could be causing Chrome to think that the mode I'm using is withCredentials = 'include'?
The answer to that is, it’s subjective — it depends on who the server admin intends the response for. But best practice is, you probably only want to send back Access-Control-Allow-Credentials: true to particular origins that you know and explicitly want to allow. That’s why the CORS protocol has the restriction that it won’t allow your frontend code to access a response if the request has credentials and the response has the Access-Control-Allow-Origin: * (wildcard) header value.
Should the server be sending back the Access-Control-Allow-Credentials: true header on ALL requests after the OPTIONS?
Chrome would only think that if the mode actually really is withCredentials = 'include'. So either some part of your client code is actually setting withCredentials = 'include' — or else, withCredentials = 'include' isn’t actually getting set all but you for some reason just think it is.

CORS supported & dockerized web server

Given an API server with Swagger UI, I want to run this server in a docker container and access from outside world and make sure this server supports CORS.
Note that current server implementation set response header Access-Control-Allow-Origin: <request.origin> if any Origin request header exists, otherwise it is Access-Control-Allow-Origin: *.
So, I dockerized this server and it is running on default docker interface, 0.0.0.0. On localhost or any other deployment environment, api is fine through cURL, postman etc. don't make any trouble.
When I access this dockerized server from a browser to use Swagger UI, I see errors indicating OPTIONS request wasn't successful. Unlike CLI tools, browsers send OPTIONS request before actual one. After checking developer console, I see that I get No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin is therefore not allowed access.
I also see that Origin and Host request headers are different and I believe it leads to this error. For example, Origin is http://localhost:8080 and Host is 0.0.0.0:8080 due to docker. CORS reference's relevant part also mention this. I guess modern browsers implement CORS check per this reference.
I can get rid of this by setting Access-Control-Allow-Origin: * response header for all requests but that is only a work-around and not secure.
Appreciate any guide and help.
OK, a few things:
The Origin is what indicates to the server that it's a CORS request - if the Origin request header is not passed, it's not a cross-origin request, so you don't need to send any CORS response headers.
Sending Access-Control-Allow-Origin: <value-of-Origin-request-header> (what you say you're doing) is absolutely the correct thing to do. You could add code to only send the ACAO header if the Origin request header is one you trust, but that may be getting too complex at this point.
The value of the Host header should be immaterial - as long as you send back the value of the Origin header in the ACAO header, it should work.
If you're passing cookies with your request, you'll also need to return Access-Control-Allow-Credentials: true, otherwise any Set-Cookie response headers will not be processed by a browser.
To the main problem (I think) - supporting OTPIONS requests:
You are correct that browsers may send a CORS preflight OPTIONS request, whereas non-browsers do not. If so, you'll need to do the following:
Ensure your server supports OPTIONS requests (by default, many web servers only support GET, POST and HEAD out of the box)
Return the exact same ACAO header with the response to that OPTIONS request also
Handle the other CORS request headers that get passed with the preflight OPTIONS requests - Access-Control-Request-Method and Access-Control-Request-Headers.
That last bit can get tricky, but the simplest thing to do is to return the following response headers to the OPTIONS request:
Access-Control-Allow-Origin: <value-of-Origin-request-header>
Access-Control-Allow-Credentials: true
Access-Control-Allow-Method: <value-of-Access-Control-Request-Method-request-header>
Access-Control-Allow-Headers: <value-of-Access-Control-Request-Headers-request-header>
and return the following response headers with the main GET/POST request:
Access-Control-Allow-Origin: <value-of-Origin-request-header>
Access-Control-Allow-Credentials: true

When should I really set "Access-Control-Allow-Credentials" to "true" in my response headers?

MDN says, when the credentials like cookies, authorisation header or TLS client certificates has to be exchanged between sites Access-Control-Allow-Crendentials has to be set to true.
Consider two sites A - https://example1.xyz.com and another one is B- https://example2.xyz.com. Now I have to make a http Get request from A to B. When I request B from A I am getting,
"No 'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://example1.xyz.com' is therefore not allowed
access."
So, I'm adding the following response headers in B
response.setHeader("Access-Control-Allow-Origin", request.getHeader("origin"));
This resolves the same origin error and I'm able to request to B. When and why should I set
response.setHeader("Access-Control-Allow-Credentials", "true");
When I googled to resolve this same-origin error, most of them recommended using both headers. I'm not clear about using the second one Access-Control-Allow-Credentials.
When should I use both?
Why should I set Access-Control-Allow-Origin to origin obtained from request header rather than wildcard *?
Please quote me an example to understand it better.
Allow-Credentials would be needed if you want the request to also be able to send cookies. If you needed to authorize the incoming request, based off a session ID cookie would be a common reason.
Setting a wildcard allows any site to make requests to your endpoint. Setting allow to origin is common if the request matches a whitelist you've defined. Some browsers will cache the allow response, and if you requested the same content from another domain as well, this could cause the request to be denied.
Setting Access-Control-Allow-Credentials: true actually has two effects:
Causes the browser to actually allow your frontend JavaScript code to access the response if credentials are included
Causes any Set-Cookie response header to actually have the effect of setting a cookie (the Set-Cookie response header is otherwise ignored)
Those effects combine with the effect that setting XMLHttpRequest.withCredentials or credentials: 'include' (Fetch API) have of causing credentials (HTTP cookies, TLS client certificates, and authentication entries) to actually be included as part of the request.
https://fetch.spec.whatwg.org/#example-cors-with-credentials has a good example.
Why should I set Access-Control-Allow-Origin to origin obtained from request header rather than wildcard *?
You shouldn’t unless you’re very certain what you’re doing.
It’s actually safe to do if:
The resource for which you’re setting the response headers that way is a public site or API endpoint intended to be accessible by everyone, and
You’re just not setting cookies that could enable an attacker to get access to sensitive information or confidential data.
For example, if your server code is just setting cookies just for the purpose of saving application state or session state as a convenience to your users, then there’s no risk in taking the value of the Origin request header and reflecting/echoing it back in the Access-Control-Allow-Origin value while also sending the Access-Control-Allow-Credentials: true response header.
On the other hand, if the cookies you’re setting expose sensitive information or confidential data, then unless you’re really certain you have things otherwise locked down (somehow…) you really want to avoid reflecting the Origin back in the Access-Control-Allow-Origin value (without checking it on the server side) while also sending Access-Control-Allow-Credentials: true.
If you do that, you’re potentially exposing sensitive information or confidential data in way that could allow malicious attackers to get to it. For an explanation of the risks, read the following:
https://web-in-security.blogspot.jp/2017/07/cors-misconfigurations-on-large-scale.html
http://blog.portswigger.net/2016/10/exploiting-cors-misconfigurations-for.html
And if the resource you’re sending the CORS headers for is not a public site or API endpoint intended to be accessible by everyone but is instead inside an intranet or otherwise behind some IP-address-restricted firewall, then you definitely really want to avoid combining Access-Control-Allow-Origin-reflects-Origin and Access-Control-Allow-Credentials: true. (In the intranet case you almost always want to only be allowing specific hardcoded/whitelisted origins.)

In the respective of security, is it meaningful to allow CORS for specific domains?

We can set and allow cross-origin-resource-sharing for
All domains , Specific domains and Not allow for any domains
But I wonder allowing CORS for specific domains meaningful.
If a hacker knows the domains that server allows. (e.g www.facebook.com)
Hacker can fake the origin header in the request as www.facebook.com
Thus, in the perspective of security. I think only Allow all domains and Not allow for any domains are meaningful. Since it is very easy to fake the origin of the requester
Am I right??
Browsers are where CORS restrictions are enforced. And browsers know the real origin a script runs in. That’s how they work. If they didn’t, there would be zero security on the Web.
So browsers do CORS checks against what they know to be the real origin of the JavaScript code that’s making an XHR or fetch() request—not against the value of the Origin header.
And browsers are what set the Origin request header and send it over the network to begin with. Browsers set the Origin value based on what they know to be the real origin, and not for their own use—because they already know what the origin is and that value is what they use internally.
So even if you manage to change an Origin header a browser sends over the network, that won’t matter to the browser—it’s going to ignore that value and continue checking against the real origin.
More details
As far as CORS goes, servers just send back documents, with an Access-Control-Allow-Origin header and other CORS headers, to any client that requests them.
Consider if you use curl or something to request a document from a server: The server doesn’t check the Origin header and refuse to send the document if the requesting origin doesn’t match the Access-Control-Allow-Origin header. The server sends the response regardless.
And as far as clients go, curl and non-browser tools don’t have the concept of an origin to begin with and so don’t usually send any Origin header to begin with. You can make curl send one—with any value you want—but it’s pointless because servers don’t care what the value is.
And curl, etc., don’t check the value of the Access-Control-Allow-Origin response header the server sends, and refuse to get a document if the request’s Origin header doesn’t match the Access-Control-Allow-Origin header in the server response. They just get the document.
But browsers are different. Browser engines are really the only clients that have the notion of an origin to begin with, and that know the actual origin a Web application’s JavaScript is running in.
And unlike curl, etc., browsers will not let your script get a document if the XHR or fetch() call requesting it is from an origin not allowed in the server’s Access-Control-Allow-Origin header.
And again, the way browsers determine what the origin is by already knowing what the origin is, not based on the value of whatever Origin request header might end up getting sent in the request.

CORS - why is Access-control-allow-origin header necessary?

Premise:
When an site (http://example.com) tries to make a cross-origin request, the browser will send an HTTP request to the cross-origin server (http://other-server.com) with the header Origin: http://example.com. If the server at http://other-server.com approves http://example.com as a valid origin, then it will 1) Respond without error AND 2) set the response header to Access-control-allow-origin: http://example.com
My question is - why is it necessary to set the Access-control-allow-origin header in the response? Doesn't responding without error already acknowledge that the server (http://other-server.com) is allowing the cross-origin request?
This extra layer of acknowledgement gives servers a lot of flexibility over how they support CORS. For example:
1) A server has a lot of choices when setting the Access-Control-Allow-Origin header. It can use the * value to allow all clients, or it can limit the scope of clients by using the actual value of the origin (e.g. http://example.com). If a server does support CORS, but not for all origins, it could respond without error, but the Access-Control-Allow-Origin could be set to http://notyourorigin.com.
2) CORS allows even more flexibility via the Access-Control-Allow-Methods and Access-Control-Allow-Headers preflight response headers. These headers go beyond the simple binary success/error HTTP status, and provide more nuanced information about what is and is not supported in the server.
As the examples above point out, an error response without any context can be very confusing to the user. If you make a CORS request, and all you get back is an error response, you have no idea why that request failed. Are you doing the request wrong? Does the server support CORS at all? This can be very difficult to figure out without any accompanying information. The Access-Control-* gives more context to the user so they can effectively debug their CORS requests.

Resources