CORS supported & dockerized web server - http

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

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

No ‘Access-Control-Allow-Origin’ header is present using angular + WordPress

when i post the data using POST method in woocommerce api. i am getting cors issue
Access to fetch at 'http://localhost/wordpress/wc-api/v3/customers?oauth_consumer_key=ck_64d88e1fa3516e9f5a06b6053f02976a534d3f8f&oauth_nonce=zsu3ysEnFHhvrZt4Nc7H66Dgu28H20K7&oauth_signature_method=HMAC-SHA256&oauth_timestamp=1562587817&oauth_version=1.0&oauth_signature=KtFxvyQNklUlfCi6rNWyJ0DEJ6AS2ZbwbO44u%2FEqxG4%3D' from origin 'http://localhost:8100' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
You have to set a Access-Control-Allow-Origin header on each request to the server, if your server is on a different domain than the app on which you are making those requests (the server sets it as a response header). Adding that header tells the system that the external domain "localhost:8100" is allowed to make those requests.
You cannot circumvent this requirement in vanilla browsers, because it is a built in security feature to reduce CORS attacks
PS. different ports on the same domain are considered to be different domains. Thus example.com will get a 401 error, if you are making a request to example.com:8100. Same goes for localhost, or any other domain.
Example code from an Apache2 web server .conf file, that I personally use to set these headers.
SetEnvIf Origin "^http(s)?://(.+\.)?(staging.\xxx\.com|xxx\.com|xxx\.local|xxx\.local:4200|a2\.local)$" origin_is=$0
Header always set Access-Control-Allow-Origin %{origin_is}e env=origin_is
Just replace the xxx.com domains with localhost:8100 or whatever else you need in that array. (if you are using Apache web server)
As a result, the Chrome network tab should have an Access-Control-Allow-Origin header on attached to the request

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.

Why is an OPTIONS request sent and can I disable it?

I am building a web API. I found whenever I use Chrome to POST, GET to my API, there is always an OPTIONS request sent before the real request, which is quite annoying. Currently, I get the server to ignore any OPTIONS requests. Now my question is what's good to send an OPTIONS request to double the server's load? Is there any way to completely stop the browser from sending OPTIONS requests?
edit 2018-09-13: added some precisions about this pre-flight request and how to avoid it at the end of this reponse.
OPTIONS requests are what we call pre-flight requests in Cross-origin resource sharing (CORS).
They are necessary when you're making requests across different origins in specific situations.
This pre-flight request is made by some browsers as a safety measure to ensure that the request being done is trusted by the server.
Meaning the server understands that the method, origin and headers being sent on the request are safe to act upon.
Your server should not ignore but handle these requests whenever you're attempting to do cross origin requests.
A good resource can be found here http://enable-cors.org/
A way to handle these to get comfortable is to ensure that for any path with OPTIONS method the server sends a response with this header
Access-Control-Allow-Origin: *
This will tell the browser that the server is willing to answer requests from any origin.
For more information on how to add CORS support to your server see the following flowchart
http://www.html5rocks.com/static/images/cors_server_flowchart.png
edit 2018-09-13
CORS OPTIONS request is triggered only in somes cases, as explained in MDN docs:
Some requests don’t trigger a CORS preflight. Those are called “simple requests” in this article, though the Fetch spec (which defines CORS) doesn’t use that term. A request that doesn’t trigger a CORS preflight—a so-called “simple request”—is one that meets all the following conditions:
The only allowed methods are:
GET
HEAD
POST
Apart from the headers set automatically by the user agent (for example, Connection, User-Agent, or any of the other headers with names defined in the Fetch spec as a “forbidden header name”), the only headers which are allowed to be manually set are those which the Fetch spec defines as being a “CORS-safelisted request-header”, which are:
Accept
Accept-Language
Content-Language
Content-Type (but note the additional requirements below)
DPR
Downlink
Save-Data
Viewport-Width
Width
The only allowed values for the Content-Type header are:
application/x-www-form-urlencoded
multipart/form-data
text/plain
No event listeners are registered on any XMLHttpRequestUpload object used in the request; these are accessed using the XMLHttpRequest.upload property.
No ReadableStream object is used in the request.
Have gone through this issue, below is my conclusion to this issue and my solution.
According to the CORS strategy (highly recommend you read about it) You can't just force the browser to stop sending OPTIONS request if it thinks it needs to.
There are two ways you can work around it:
Make sure your request is a "simple request"
Set Access-Control-Max-Age for the OPTIONS request
Simple request
A simple cross-site request is one that meets all the following conditions:
The only allowed methods are:
GET
HEAD
POST
Apart from the headers set automatically by the user agent (e.g. Connection, User-Agent, etc.), the only headers which are allowed to be manually set are:
Accept
Accept-Language
Content-Language
Content-Type
The only allowed values for the Content-Type header are:
application/x-www-form-urlencoded
multipart/form-data
text/plain
A simple request will not cause a pre-flight OPTIONS request.
Set a cache for the OPTIONS check
You can set a Access-Control-Max-Age for the OPTIONS request, so that it will not check the permission again until it is expired.
Access-Control-Max-Age gives the value in seconds for how long the response to the preflight request can be cached for without sending another preflight request.
Limitation Noted
For Chrome, the maximum seconds for Access-Control-Max-Age is 600 which is 10 minutes, according to chrome source code
Access-Control-Max-Age only works for one resource every time, for example, GET requests with same URL path but different queries will be treated as different resources. So the request to the second resource will still trigger a preflight request.
Please refer this answer on the actual need for pre-flighted OPTIONS request: CORS - What is the motivation behind introducing preflight requests?
To disable the OPTIONS request, below conditions must be satisfied for ajax request:
Request does not set custom HTTP headers like 'application/xml' or 'application/json' etc
The request method has to be one of GET, HEAD or POST. If POST, content type should be one of application/x-www-form-urlencoded, multipart/form-data, or text/plain
Reference:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
When you have the debug console open and the Disable Cache option turned on, preflight requests will always be sent (i.e. before each and every request). if you don't disable the cache, a pre-flight request will be sent only once (per server)
Yes it's possible to avoid options request. Options request is a preflight request when you send (post) any data to another domain. It's a browser security issue. But we can use another technology: iframe transport layer. I strongly recommend you forget about any CORS configuration and use readymade solution and it will work anywhere.
Take a look here:
https://github.com/jpillora/xdomain
And working example:
http://jpillora.com/xdomain/
For a developer who understands the reason it exists but needs to access an API that doesn't handle OPTIONS calls without auth, I need a temporary answer so I can develop locally until the API owner adds proper SPA CORS support or I get a proxy API up and running.
I found you can disable CORS in Safari and Chrome on a Mac.
Disable same origin policy in Chrome
Chrome: Quit Chrome, open an terminal and paste this command: open /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir
Safari: Disabling same-origin policy in Safari
If you want to disable the same-origin policy on Safari (I have 9.1.1), then you only need to enable the developer menu, and select "Disable Cross-Origin Restrictions" from the develop menu.
As mentioned in previous posts already, OPTIONS requests are there for a reason. If you have an issue with large response times from your server (e.g. overseas connection) you can also have your browser cache the preflight requests.
Have your server reply with the Access-Control-Max-Age header and for requests that go to the same endpoint the preflight request will have been cached and not occur anymore.
I have solved this problem like.
if($_SERVER['REQUEST_METHOD'] == 'OPTIONS' && ENV == 'devel') {
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: X-Requested-With');
header("HTTP/1.1 200 OK");
die();
}
It is only for development. With this I am waiting 9ms and 500ms and not 8s and 500ms. I can do that because production JS app will be on the same machine as production so there will be no OPTIONS but development is my local.
You can't but you could avoid CORS using JSONP.
you can also use a API Manager (like Open Sources Gravitee.io) to prevent CORS issues between frontend app and backend services by manipulating headers in preflight.
Header used in response to a preflight request to indicate which HTTP headers can be used when making the actual request :
content-type
access-control-allow-header
authorization
x-requested-with
and specify the "allow-origin" = localhost:4200 for example
After spending a whole day and a half trying to work through a similar problem I found it had to do with IIS.
My Web API project was set up as follows:
// WebApiConfig.cs
public static void Register(HttpConfiguration config)
{
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
//...
}
I did not have CORS specific config options in the web.config > system.webServer node like I have seen in so many posts
No CORS specific code in the global.asax or in the controller as a decorator
The problem was the app pool settings.
The managed pipeline mode was set to classic (changed it to integrated) and the Identity was set to Network Service (changed it to ApplicationPoolIdentity)
Changing those settings (and refreshing the app pool) fixed it for me.
OPTIONS request is a feature of web browsers, so it's not easy to disable it. But I found a way to redirect it away with proxy. It's useful in case that the service endpoint just cannot handle CORS/OPTIONS yet, maybe still under development, or mal-configured.
Steps:
Setup a reverse proxy for such requests with tools of choice (nginx, YARP, ...)
Create an endpoint just to handle the OPTIONS request. It might be easier to create a normal empty endpoint, and make sure it handles CORS well.
Configure two sets of rules for the proxy. One is to route all OPTIONS requests to the dummy endpoint above. Another to route all other requests to actual endpoint in question.
Update the web site to use proxy instead.
Basically this approach is to cheat browser that OPTIONS request works. Considering CORS is not to enhance security, but to relax the same-origin policy, I hope this trick could work for a while. :)
One solution I have used in the past - lets say your site is on mydomain.com, and you need to make an ajax request to foreigndomain.com
Configure an IIS rewrite from your domain to the foreign domain - e.g.
<rewrite>
<rules>
<rule name="ForeignRewrite" stopProcessing="true">
<match url="^api/v1/(.*)$" />
<action type="Rewrite" url="https://foreigndomain.com/{R:1}" />
</rule>
</rules>
</rewrite>
on your mydomain.com site - you can then make a same origin request, and there's no need for any options request :)
It can be solved in case of use of a proxy that intercept the request and write the appropriate headers.
In the particular case of Varnish these would be the rules:
if (req.http.host == "CUSTOM_URL" ) {
set resp.http.Access-Control-Allow-Origin = "*";
if (req.method == "OPTIONS") {
set resp.http.Access-Control-Max-Age = "1728000";
set resp.http.Access-Control-Allow-Methods = "GET, POST, PUT, DELETE, PATCH, OPTIONS";
set resp.http.Access-Control-Allow-Headers = "Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since";
set resp.http.Content-Length = "0";
set resp.http.Content-Type = "text/plain charset=UTF-8";
set resp.status = 204;
}
}
What worked for me was to import "github.com/gorilla/handlers" and then use it this way:
router := mux.NewRouter()
router.HandleFunc("/config", getConfig).Methods("GET")
router.HandleFunc("/config/emcServer", createEmcServers).Methods("POST")
headersOk := handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type"})
originsOk := handlers.AllowedOrigins([]string{"*"})
methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "OPTIONS"})
log.Fatal(http.ListenAndServe(":" + webServicePort, handlers.CORS(originsOk, headersOk, methodsOk)(router)))
As soon as I executed an Ajax POST request and attaching JSON data to it, Chrome would always add the Content-Type header which was not in my previous AllowedHeaders config.

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