CORS - Chrome canceling simple GET request - nginx

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.

Related

Firefox cannot find PATCH in access-control-allow-methods

Preflight response returns headers
server: uvicorn
access-control-allow-origin: *
access-control-allow-methods: DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT
access-control-max-age: 600
access-control-allow-headers: Accept, Accept-Language, Content-Language, Content-Type
content-length: 2
content-type: text/plain; charset=utf-8
for request with headers
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Access-Control-Request-Method: PATCH
Access-Control-Request-Headers: content-type
Referer: http://localhost:8080/
Origin: http://localhost:8080
Connection: keep-alive
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
But Firefox (102.0) rejects this with reason CORS Method Not Found.
POST and DELETE methods work fine, only PATCH is rejected. In Chrome, this request passes without problems.
In FastAPI, I have a middleware configured as following:
middleware=[Middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"])],
Does anyone know what the problem may be? I also don't send any content/data in this PATCH request, I don't know if that's important.

Python requests.get returns 500 for only one specific url in Google Cloud Function, works fine in local machine

I have a google cloud function to get response of the two urls, but I got 500 for the second, the first isfine:
https://api.racing.com/v1/en-au/meet/details/5158389/
https://api.racing.com/v1/en-au/meet/details/5158194/
url = 'https://api.racing.com/v1/en-au/meet/details/5158194/'
requests.get(url, headers=headers)
And both works fine in the local machine. Any ideas? Thanks
Updates:
So far, I have tried set TLS and cache-control in the header, none works:
class Tls12HttpAdapter(HTTPAdapter):
def init_poolmanager(self, connections, maxsize, block=False):
self.poolmanager = PoolManager(
num_pools=connections, maxsize=maxsize,
block=block, ssl_version=ssl.PROTOCOL_TLSv1_2)
s = requests.Session()
s.mount(url, Tls12HttpAdapter())
r = s.get(url, headers=headers)
I also tried using curl to get the output:
result = subprocess.run("curl -i --tlsv1.2 https://api.racing.com/v1/en-au/meet/details/5158389/", shell=True, capture_output=True)
I found the difference between the working url and non-working url is in the cache-control field:
the working one:
CompletedProcess(args='curl -i --tlsv1.2 https://api.racing.com/v1/en-au/meet/details/5158389/',
returncode=0, stdout=b'HTTP/2 200 \r\naccess-control-allow-headers: content-Type, accept, origin, X-Requested-With,
DataServiceVersion, MaxDataServiceVersion\r\naccess-control-allow-methods: PUT, POST, GET, DELETE, MERGE, OPTIONS\r\naccess-control-allow-origin: *\r\n
cache-control: public, max-age=180\r\ncontent-type: text/javascript; charset=utf-8\r\n
x-aspnet-version: 4.0.30319\r\nx-powered-by: ASP.NET\r\ndate: Sun, 08 Dec 2019 23:26:38 GMT\r\nx-device: default\r\n\r\n{\r\n "meet": {
the non-working one:
CompletedProcess(args='curl -i --tlsv1.2 https://api.racing.com/v1/en-au/meet/details/5158194/', returncode=0, stdout=b'HTTP/2 500 \r\naccess-control-allow-headers: content-Type, accept, origin, X-Requested-With,
DataServiceVersion, MaxDataServiceVersion\r\naccess-control-allow-methods: PUT, POST, GET, DELETE, MERGE, OPTIONS\r\naccess-control-allow-origin: *\r\n
cache-control: no-cache\r\ncontent-type: text/javascript; charset=utf-8\r\nexpires: -1\r\npragma: no-cache\r\nx-aspnet-version: 4.0.30319\r\nx-powered-by: ASP.NET\r\n
content-length: 36\r\ndate: Sun, 08 Dec 2019 23:22:35 GMT\r\nx-device: default\r\n\r\n{"Message":"An error has occurred."}'
The requests.get() for the two urls both works in the local machine, but the second won't work in google cloud function, means the google server sends the http request differently? or the racing server treats the two url differently?
I reproduced the issue, I found what is the problem, but I wasn't able to solve it (because of my low python skill).
I got an error 500 by doing this with curl:
curl -i --tlsv1.0 https://api.racing.com/v1/en-au/meet/details/5158194/
It works with --tlsv1.1 and --tlsv1.2. The version of TLS used should be the issue.
I tried to force an higher version of requests in the requirements.txt file without change. I added the parameter verify=False to the get request without better results.
I'm only at the half way. I hope this help you for going further in your debug.
EDIT
This morning (12-09-2019), I retried on Cloud Shell, and all tls versions are working:
guillaume_blaquiere#cloudshell:~😎 curl -i --tlsv1.0 https://api.racing.com/v1/en-au/meet/details/5158194/ | head
HTTP/1.1 200 OK
Access-Control-Allow-Headers: content-Type, accept, origin, X-Requested-With, DataServiceVersion, MaxDataServiceVersion
Access-Control-Allow-Methods: PUT, POST, GET, DELETE, MERGE, OPTIONS
Access-Control-Allow-Origin: *
Cache-Control: public, max-age=180
Content-Type: text/javascript; charset=utf-8
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
guillaume_blaquiere#cloudshell:~😎 curl -i --tlsv1.1 https://api.racing.com/v1/en-au/meet/details/5158194/ | head
HTTP/1.1 200 OK
Access-Control-Allow-Headers: content-Type, accept, origin, X-Requested-With, DataServiceVersion, MaxDataServiceVersion
Access-Control-Allow-Methods: PUT, POST, GET, DELETE, MERGE, OPTIONS
Access-Control-Allow-Origin: *
Cache-Control: public, max-age=180
Content-Type: text/javascript; charset=utf-8
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
guillaume_blaquiere#cloudshell:~😎 curl -i --tlsv1.2 https://api.racing.com/v1/en-au/meet/details/5158194/ | head
HTTP/2 200
access-control-allow-headers: content-Type, accept, origin, X-Requested-With, DataServiceVersion, MaxDataServiceVersion
access-control-allow-methods: PUT, POST, GET, DELETE, MERGE, OPTIONS
access-control-allow-origin: *
cache-control: public, max-age=180
content-type: text/javascript; charset=utf-8
x-aspnet-version: 4.0.30319
x-powered-by: ASP.NET
guillaume_blaquiere#cloudshell:~😎 curl -i --tlsv1 https://api.racing.com/v1/en-au/meet/details/5158194/ | head
HTTP/2 200
access-control-allow-headers: content-Type, accept, origin, X-Requested-With, DataServiceVersion, MaxDataServiceVersion
access-control-allow-methods: PUT, POST, GET, DELETE, MERGE, OPTIONS
access-control-allow-origin: *
cache-control: public, max-age=180
content-type: text/javascript; charset=utf-8
x-aspnet-version: 4.0.30319
x-powered-by: ASP.NET
Maybe an update on the API side? My function still returns 500. Not understandable!
Anyway, curl and subprocess in Cloud Function aren't recommended, you don't manage the underlying infrastructure (the main principle of serverless) and you don't know what to expect when you use system command.
Final Update:
I changed the region of my cloud function, and it worked.
Still don't understand why, my guess is the IP address in Asian region is blocked from the server, anyhow we live in a biased world.

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/

Cors Blocks Request with status 403 on 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';

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