Cookies are not available in JavaScript (and development tools), but are sent along with the XHR request (without using httponly)

I use both the front-end and back-end applications in another domain with session-based authorization. I set up a working CORS configuration that works as expected on localhost (e.g. from port :9000 to port :8080 ). As soon as I deploy applications to secure domains (both domains only allow HTTPS), the CSRF cookie is no longer available in JavaScript, which leads to an incorrect subsequent front-end request (the CSRF header is missing).

A cookie is set by the content in the Set-Cookie header without using the HttpOnly flag. It is actually set somewhere in the browser because the subsequent request contains both a session cookie and a CSRF cookie. Attempting to access it using JavaScript (using, for example, document.cookie in the console) returns an empty string. In DevTools from Chrome cookies are not displayed in the front-end domain (the internal domain is not even specified).

I expect the cookie to be set and displayed in the current domain (front-end domain). I use the withCredentials library withCredentials flag .

Do you have any ideas why a cookie cannot be accessed from JavaScript or from DevTools in Chrome? Does this have anything to do with the Strict-Transport-Security header?


Headings

1. The start header of the GET response

 HTTP/1.1 401 Unauthorized Access-Control-Allow-Credentials: true Access-Control-Allow-Origin: https://[my-frontend-domain] Cache-Control: no-cache, no-store, max-age=0, must-revalidate Content-Encoding: gzip Content-Type: application/json;charset=UTF-8 Date: Wed, 20 Sep 2017 11:57:07 GMT Expires: 0 Pragma: no-cache Server: Apache-Coyote/1.1 Set-Cookie: CSRF-TOKEN=[some-token]; Path=/ Vary: Origin,Accept-Encoding X-Content-Type-Options: nosniff X-Vcap-Request-Id: [some-token] X-Xss-Protection: 1; mode=block Content-Length: [some-length] Strict-Transport-Security: max-age=15768000; includeSubDomains 

2. Subsequent POST request header

 POST /api/authentication HTTP/1.1 Host: [my-backend-host] Connection: keep-alive Content-Length: [some-length] Pragma: no-cache Cache-Control: no-cache Accept: application/json, text/plain, */* Origin: [my-frontend-host] User-Agent: [Google-Chrome-User-Agent] Content-Type: application/x-www-form-urlencoded DNT: 1 Referer: [my-frontend-host] Accept-Encoding: gzip, deflate, br Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4,de-CH;q=0.2,it;q=0.2 Cookie: [some-other-cookies]; CSRF-TOKEN=[same-token-as-in-the-previous-request] 

This request should contain the CSRF header, which will be automatically added if the cookie was accessible using JavaScript.

+8
javascript cookies cors hsts
source share
4 answers

In short, it is not possible to access cookies with cross-code, document.cookie can only access the current ( or parent ) domain cookies.

The hint for being the main reason was ssc-hrep3, which mentioned "both domains" in its question.

It is very easy to make this mistake when switching from a local deployment, using only different ports for internal and external servers, to one that uses two different hosts. This will work locally since cookies are shared between ports and will fail when using two different hosts. (Unlike some other CORS issues that will also be displayed locally)

See ssc-hrep3 answer for more information and a workaround.

+1
source

one

You may need to add Access-Control-Allow-Headers to allow the transfer of certain headers.

Please try adding the following response headers to your server (OPTIONS method) for testing purposes.

 Access-Control-Allow-Headers: Content-Type, * 

In production, I recommend limiting the headers to the following (but I'm not 100% sure of the correct list of headers, you need to experiment here if it works)

 Access-Control-Allow-Headers: Cookie, Set-Cookie 

See this for the link https://quickleft.com/blog/cookies-with-my-cors/

2

Another problem you may experience is that your cookies will be set in this domain where your backend service is located (and not in the domain from which you request)

Please check this as well

3

As an option of the latter problem, the browser may prohibit the installation of cookies for the domain b.xxx.com from a request that comes from a.xxx.com

In this case, you can try to set a cookie in the parent domain xxx.com , so it will be available for your client side

+4
source

TL; DR . Reading access to cross-domain cookies is not possible. Solving the problem of adding a CSRF token to the response header. Another solution to completely bypass CORS requests and cross-domain requests would be to use a reverse proxy.


Problem

As stated in my question above, the JavaScript part of my interface (e.g. https://example1.com trying to access a non- HttpOnly cookie from my internal server, e.g. https://example2.com ). To have remote API access with JavaScript, I use CORS. This allows you to skip requests. I use withCredentials: true on the interface side and Access-Control-Allow-Credentials: true on the server side. The Set-Cookie header then sets the cookie in the source code, not the source beginning. Therefore, the cookie does not appear in DevTools or in the document.cookie command in JavaScript.

Cookies set at the source end are always part of the inner loop request via CORS. However, I need access to the CSRF cookie content to add a token to the request header (to prevent CSRF attacks). As I found out, there is no way to read (or write) cookies from another domain using JavaScript - no matter what CORS setting is used (see these StackOverflow answers: [1] , [2] ). The browser restricts access to the contents of the cookie to the origin of one domain.

Decision

This leads to the conclusion that there is no way to access the non HttpOnly cookie content of another domain. A workaround for this problem is to set the CSRF token in an optional custom response header. Usually these headers also cannot be accessed in another domain. However, they can be set as the basic setting for CORS Access-Control-Expose-Headers . This is safe if you use the strictly limited Access-Control-Allow-Origin header.

Another workaround would be to use a reverse proxy, which generally circumvents problems with CORS and cross-domain requests. Using such a reverse proxy provides a special path on the interface that will be redirected to the internal server (server side). For example, https://front-end/api calls are proxied to https://back-end/api . Since all requests from the external interface are made by the front proxy server in the same domain, the browser processes each call as a request of one domain, and cookies are directly set at the initial start. The disadvantages of this solution include potential performance problems due to the fact that the other server is located between them (delay), and cookies must be set on two sources (log in twice when directly accessing the internal content). Setting up a reverse proxy server can be done using nginx, apache, or also very easily using http-proxy-middleware in Node.js:

 var express = require('express'); var proxy = require('http-proxy-middleware'); var options = { target: 'https://[server]', changeOrigin: true, secure: true }; var exampleProxy = proxy(options); var app = express(); app.use('/api', exampleProxy); app.use(express.static(__dirname + "/public")); app.listen(process.env.PORT || 8080); 
+2
source

As you can read here , the XHR specification explicitly forbids reading Set-Cookies. The best way to do this is to pass the header information instead of a cookie.

+1
source

All Articles