Cors Blocks Request with status 403 on Nginx - nginx

I am facing a strange issue with running CORS on Nginx, CORS is working fine for everything but one scenario when the Server responds with a 403 http response.
Basically when I login with correct credentials the cors request works fine , however when I provide wrong credentials for login the server(backend) responds with a 403 status and I get the following error
"NetworkError: 403 Forbidden - http://mydomain.com/v1/login"
login
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://mydomain.com/v1/login. This can be fixed by moving the resource to the same domain or enabling CORS.
If the credentials are correct I don't get this error and everything works perfectly.
I have done the configuration for enabling CORS and it seems to be working fine for everything else.
Following are the Request Headers
Request Headers
User-Agent:Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:29.0) Gecko/20100101 Firefox/29.0
Referer:http://abc.mydomain.com/
Pragma: no-cache
Origin: http://abc.mydomain.com
Host: www.mydomain.com
Content-Type: application/json;charset=utf-8
Content-Length: 74
Connection: keep-alive
Cache-Control: no-cache
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Accept: application/json, text/plain, /
Response Headers
Server: nginx/1.4.1
Date: Tue, 10 Jun 2014 05:28:30 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 76
Connection: keep-alive

An option for nginx(>=1.75) is to specify always parameter in add_header :
If the always parameter is specified (1.7.5), the header field will be
added regardless of the response code.

I assume that you are using add_header directive to add CORS headers in the nginx configuration.
Nginx modules reference states about add_header:
Adds the specified field to a response header provided that the response code equals 200, 201, 204, 206, 301, 302, 303, 304, or 307.
To fix problem you could use ngx_headers_more module to set CORS headers also to error responses.
more_set_headers 'Access-Control-Allow-Origin: $http_origin';
more_set_headers 'Access-Control-Allow-Headers: Content-Type, Origin, Accept, Cookie';

Related

CORS preflight from https site to http localhost

I'm using Brave right now (but I could reproduce the issue in Chrome as well.)
Scenario:
I have a control panel which served via Cloudflare on https. This should make a call to a service running locally http://localhost:39031. The local server served by go-chi and using it's CORS middleware. When the local server get's the response it returns 200 back to the control panel for the Preflight request.
2022-08-25T14:59:30+02:00 [DEBUG] Handler: Preflight request%!(EXTRA []interface {}=[])
2022-08-25T14:59:30+02:00 [DEBUG] Preflight response headers: [map[Access-Control-Allow-Credentials:[true] Access-Control-Allow-Methods:[GET] Access-Control-Allow-Origin:[*] Access-Control-Max-Age:[300] Vary:[Origin Access-Control-Request-Method Access-Control-Request-Headers]]]
2022/08/25 14:59:30 "OPTIONS http://localhost:39031/hc HTTP/1.1" from 127.0.0.1:43378 - 200 0B in 132.937µs
So it should be fine, but I get a message like this in the browser (which is a warning):
A cross-origin resource sharing (CORS) request was blocked because the response to the associated preflight request failed, had an unsuccessful HTTP status code, and/or was a redirect.
To fix this issue, ensure all CORS preflight OPTIONS requests are answered with a successful HTTP status code (2xx) and do not redirect.
What make no sense, that it's doing the GET request otherwise, and I'm getting back the result properly. Does it maybe because the disabled shield?
I don't see what I did which results this behavior. I also sent OPTIONS via Postman and it turns out that the response satisfy the needs of a preflight response. I already read nearly everything from Preflight response and handling.
This is the Preflight Request:
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Access-Control-Request-Method: GET
Access-Control-Request-Private-Network: true
Connection: keep-alive
Host: localhost:39031
Origin: https://sandbox.XXX.com
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.81 Safari/537.36

Setting JSON request header in Angular2 HTTP POST

I'm having a problem setting a content-type of application/json header on my post request.
saveUpdates(alltabs: AllTabs): Observable<Response> {
let api = this.host + this.routes.save;
let headers = new Headers();
headers.append('Content-Type', 'application/json');
return this._http.post(api, JSON.stringify(alltabs), { headers: headers })
.map((response: Response) => <Response>response.json())
.do(data => console.log("saveUpdates(): " + data))
.catch(this.handleError);
}
Request Headers:
OPTIONS /api/productsave HTTP/1.1
Host: wbtest:92
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Access-Control-Request-Method: POST
Origin: http://localhost:3000
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.84 Safari/537.36
Access-Control-Request-Headers: content-type
Accept: */*
Referer: http://localhost:3000/product/60000010080
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
Response Headers:
HTTP/1.1 405 Method Not Allowed
Cache-Control: no-cache
Pragma: no-cache
Allow: POST
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Access-Control-Allow-Origin: *
Date: Tue, 14 Jun 2016 15:16:15 GMT
Content-Length: 76
As you can see, my request has two unexpected headers added "Access-Control-Request-Headers" and "Access-Control-Request-Method". This seems to suggest an issue with CORS (Cross-Origin Resource Sharing). However, the web.conf file on the API server has been working and the response headers states "Access-Control-Allow-Origin: *".
Any idea what could be wrong?
UPDATE:
The above code is correct - the problem is with the Sever code not being configured to handle preflight requests. In my case, the .NET Web API 2 application was not configured to allow CORS.
With CORS, you have two kinds of requests. As a matter of fact, the CORS specification distinguishes two distinct use cases:
Simple requests. This use case applies if we use HTTP GET, HEAD and POST methods. In the case of POST methods, only content types with the following values are supported: text/plain, application/x-www-form-urlencoded and multipart/form-data.
Preflighted requests. When the "simple requests" use case doesn't apply, a first request (with the HTTP OPTIONS method) is made to check what can be done in the context of cross-domain requests.
It seems that your server isn't configured to support preflighted request. The reason for the 405 status code (405 Method Not Allowed).
See this article for more details:
http://restlet.com/blog/2015/12/15/understanding-and-using-cors/

How do I set the request accept header in nginx?

I need to set the request accept header for any URLs on certain path to */*.
Here are the request accept headers being set currently:
GET /apiQuery/preview?view=location_csv&q=name:* HTTP/1.1
Host: www.blah.com
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
I want it to be:
GET /apiQuery/preview?view=location_csv&q=name:* HTTP/1.1
Host: www.blah.com
Connection: keep-alive
Cache-Control: max-age=0
Accept: */*
..for any URLs along the /apiQuery/ path.
There is a module available for such a purpose called HeaderMode.
more_set_input_headers 'Accept: */*';
Downside: This module is not distributed with Nginx. You will have to follow the installation guide to get this module integrated with your Nginx.
Alternatively, if you are using your Nginx as a proxy, you should use proxy_set_header directive.
proxy_set_header Accept '*/*';

CORS - Chrome canceling simple GET request

I'm trying to set up CORS to work with Socket IO in Node. Unfortunately the request keeps getting cancelled by Chrome:
GET https://example.com/zebra/8601/socket.io/1/?key=example123&t=1377831596484 HTTP/1.1
Origin: https://example.io
Referer: https://example.io/example
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.57 Safari/537.36
If I open up the URL in a new tab, I get a much nicer response:
HTTP/1.1 200 OK
Server: nginx/1.4.1
Date: Fri, 30 Aug 2013 03:00:12 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, PUT, POST, OPTIONS
Access-Control-Allow-Headers: Authorization,Content-Type,Accept,Origin,Referer,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since
While it's an actual cross-origin request though, it just fails.
The Nginx config is as follows:
location ~ ^/zebra/(\d+)(?:/(.*))?$ {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Methods "GET, PUT, POST, OPTIONS";
add_header Access-Control-Allow-Headers "Authorization,Content-Type,Accept,Origin,Referer,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since";
}
How can I configure things to get it working with CORS?
So the solution was to make sure we were configuring this Socket IO setting correctly at the Node JS end:
io.configure(function() {
io.set('origins', '*:*');
});
.. and then not do anything funky with CORS at the Nginx end, at all.
Removing all add_header Access-Control-Allow occurrences from the Nginx config has done the trick.

Cross-domain chunked uploads using CORS

I have user-submitted files that I'm trying to upload in 10 MB chunks. I'm currently using raw XMLHttpRequest (and XDomainRequest) to push each individual slice (File.prototoype.slice) on the front end. The back end is Nginx using the upload module.
Just for reference, here's the synopsis of how I'm using slice:
element.files[0].slice(...)
I understand the cross-browser prefixed methods webkitSlice and mozSlice and all that.
The problem I have is with actually making the cross-domain request. I'm uploading from server.local to upload.server.local. In Firefox, the options request goes through fine and then the actual post fails. In Chrome and Opera, the options request fails with
OPTIONS https://URL Resource failed to load
Here are the headers from Firefox:
Request Headers
OPTIONS /path/to/asset HTTP/1.1
Host: upload.server.local:8443
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:18.0) Gecko/20100101 Firefox/18.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Origin: https://server.local:8443
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-disposition,content-type,x-content-range,x-session-id
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Response Headers
HTTP/1.1 204 No Content
Server: nginx/1.2.6
Date: Wed, 13 Feb 2013 03:27:44 GMT
Connection: keep-alive
access-control-allow-origin: https://server.local:8443
Access-Control-Allow-Methods: POST, OPTIONS
Access-Control-Allow-Headers: x-content-range, origin, content-disposition, x-session-id, content-type, cache-control, pragma, referrer, host
access-control-allow-credentials: true
Access-Control-Max-Age: 10000
The actual post request never leaves the browser. Nginx access logs never see the post. The browser halts it for some reason. How do I unravel why this post is being blocked?
Chromium 24
Firefox 18
Opera 12.14
I've verified all browsers support CORS properly here.
By pointing my uploads to https://cors-test.appspot.com/test, I have confirmed that the problem is definitely with the server-side headers.
The POST won't leave the browser if the preflight check does not return sufficient permissions and thus the POST request is not fully authorized. The request/response included in the question does look sufficient to me.
Are you sure you are setting withCredentials = true in your XMLHttpRequest?
Are you sure that you have valid (not self-signed) SSL certificates on your servers? The HTTPS might fail the CORS check even if you have added an exception for browsing the site with an invalid certificate.
Have you tried emptying your cache? You have Access-Control-Max-Age: 10000 set in your response headers. That's close to 3 hours. I know you've been working on this longer than that but while testing especially, set that header to zero instead so you don't go crazy with browser caching of old access permissions.
In general I'd start with going as permissive as possible with the CORS headers and slowly ratcheting up the the security to see where it fails. However, this is not completely straightforward. For example, according to the MDN documentation on CORS,
When responding to a credentialed request, server must specify a domain, and cannot use wild carding. The above example would fail if the header was wildcarded as: Access-Control-Allow-Origin: *
When I send the request part of your question to https://cors-test.appspot.com/test, I get back the following:
HTTP/1.1 200 OK
Cache-Control: no-cache
Access-Control-Allow-Origin: https://server.local:8443
Access-Control-Allow-Headers: content-disposition,content-type,x-content-range,x-session-id
Access-Control-Allow-Methods: POST
Access-Control-Max-Age: 0
Access-Control-Allow-Credentials: true
Cache-Control: no-cache
Expires: Fri, 01 Jan 1990 00:00:00 GMT
Content-Type: application/json
Content-Encoding: gzip
Content-Length: 35
Vary: Accept-Encoding
Date: Thu, 23 May 2013 06:37:34 GMT
Server: Google Frontend
So you can start from there and add more and more security until it breaks to figure out what is the culprit.

Resources