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

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.

Related

NextJS cors has wrong 'Access-Control-Allow-Origin' value

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

Sec-Fetch-Mode instead of Preflight

I created login FE and finished it.
And as per usual my goto for ajax was Axios. My code is as follows.
const baseUrl = http://localhost:5000/project/us-central1/api
Axios.post(
`${baseUrl}/v1/user/login`,
{ ...data },
{
headers: {
Authorization: 'Basic auth...'
}
},
).then(r => console.log(r).catch(e =>console.log(e));
Now when i try to send request to my local firebase cloud function.
I get a 400 bad request.
after checking the request, I was wondering why it wasn't sending any preflight request, which it should do(to the best of my knowledge) but instead I saw a header named Sec-Fetch-Mode. I searched anywhere it's a bit abstract. And I can't seem to figure anything why my request still fails.
Is there anything Im missing in my config of axios?
My FE is running on a VSCode Plugin named live server(http://127.0.0.1:5500)
Also, my firebase cloud function has enabled cors
// cloud function expres app
cors({
origin: true
})
Any insights would be very helpful.
The OPTIONS request is actually being sent, because you are sending a cross-origin request with an Authorization header which is considered as non-simple. It doesn't show in developer tools because of a feature/bug in Chrome 76 & 77. See Chrome not showing OPTIONS requests in Network tab for more information.
The preflight request is a mechanism that allows to deny cross-origin requests on browser side if the server is not CORS aware (e.g: old and not maintained), or if it explicitly wants to deny cross-origin requests (in both cases, the server won't set the Access-Control-Allow-Origin header). What CORS does could be done on server side by checking the Origin header, but CORS actually protects the user at browser level. It blocks the disallowed cross-origin requests even before they are sent, thus reducing the network traffic, the server load, and preventing the old servers from receiving any cross-origin request by default.
On the other hand, Sec-Fetch-Mode is one of the Fetch metadata headers (Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site and Sec-Fetch-User). These headers are meant to inform the server about the context in which the request has been sent. Based on this extra information, the server is then able to determine if the request looks legitimate, or simply deny it. They exist to help HTTP servers mitigate certain types of attacks, and are not related to CORS.
For example the good old <img src="https://mybank.com/giveMoney?amount=9999999&to=evil#attacker.com"> attack could be detected on server side because the Sec-Fetch-Dest would be set to "image" (this is just a simple example, implying that the server exposes endpoints with the GET method with unsafe cookies for money operations which is obviously not the case in real life).
As a conclusion, fetch metadata headers are not designed to replace preflight requests, but rather to coexist with them since they fulfill different needs. And the 400 error has likely nothing to do with these, but rather with the request that does not comply with the endpoint specification.
You are missing a dot on your spread operator, this is the correct syntax:
{ ...data }
Note the three dots before “data”.
Please see the use of spread operators with objects here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax

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.)

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