JavaScript(和开发工具)无法访问Cookie,但随XHR请求一起发送(未使用httponly)

ssc*_*ep3 12 javascript cookies cors hsts strict-transport-security

我正在使用基于会话的授权在不同的域上使用前端和后端应用程序.我已经设置了一个有效的CORS配置,它可以按预期工作localhost(例如从端口:9000到端口:8080).一旦我在安全域上部署应用程序(两个域都只允许HTTPS),在JavaScript中就不再可以访问CSRF cookie,从而导致前端的错误后续请求(缺少CSRF头).

cookie由Set-Cookie标题中的后端设置,使用HttpOnly标志.它实际上设置在浏览器的某个位置,因为后续请求包含会话cookie和CSRF cookie.尝试通过JavaScript访问它(使用例如document.cookie在控制台中)返回一个空字符串.DevTools of Chrome不会在前端域上显示任何 cookie(后端域甚至没有列出).

我期待cookie被设置并在当前域(前端域)上可见.我正在使用axios库的withCredentials标志.

你有什么想法,为什么不能从JavaScript或Chrome中的DevTools访问cookie?这与Strict-Transport-Security标题有什么关系吗?


1.初始GET响应标头

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
Run Code Online (Sandbox Code Playgroud)

2.后续POST请求标头

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]
Run Code Online (Sandbox Code Playgroud)

此请求应包含CSRF标头,如果可以使用JavaScript访问cookie,则会自动添加该标头.

Ido*_*.Co 6

总之,不能访问跨域cookies,document.cookie只能访问当前(或父)域cookies。

这是根本原因的暗示,是 ssc-hrep3 在他的问题中提到了“两个域”。

当从 localhost 部署切换到只使用后端和前端服务器的不同端口,切换到使用两个不同主机的部署时,很容易犯这个错误。这将在本地工作,因为 cookie 是跨端口共享的,并且在使用两个不同的主机时会失败。(与其他一些也会在本地公开的 CORS 问题不同)

有关更多信息和解决方法,请参阅ssc-hrep3 的答案


ssc*_*ep3 5

TL; DR:无法读取跨域cookie。将CSRF令牌添加到响应标头将是一个解决方案。完全规避CORS和跨域请求的另一种解决方案是使用反向代理。


问题

正如我在上面的问题中所述,前端的JavaScript部分(例如,https://example1.com正在尝试HttpOnly从后端访问非cookie https://example2.com。为了能够使用JavaScript访问远程API,我正在使用CORS。这样一来,请求就可以通过了。我withCredentials: true在前端侧和Access-Control-Allow-Credentials: true后端侧使用Set-Cookie标题,然后将Cookie设置在后端原点而不是前端原点上。 Cookie在DevTools和document.cookieJavaScript命令中都不可见。

在后端源上设置的Cookie始终是通过CORS向后端发出的请求的一部分。但是,我需要访问CSRF cookie 的内容,以将令牌添加到请求标头中(以防止CSRF攻击)。正如我发现的那样,无论使用哪种CORS设置,都无法使用JavaScript从其他域读取(或写入)Cookie(请参阅以下StackOverflow答案:[1][2])。浏览器将访问Cookie的内容限制为来自相同域的来源。

解决方案

由此得出结论,不可能访问HttpOnly其他域的非cookie 的内容。解决此问题的方法是将CSRF令牌设置为其他自定义响应标头。这些标头通常也不能被其他域访问。但是,它们可以通过后端的CORS设置公开Access-Control-Expose-Headers。只要使用严格限制的Access-Control-Allow-Origin标头,这是安全的。

另一个解决方法是使用反向代理,它完全规避了CORS和跨域请求的问题。使用这样的反向代理在前端提供了一个特殊的路径,它将被重定向到后端(服务器端)。例如,呼叫https://front-end/api会代入https://back-end/api。由于来自前端的所有请求都发送到同一域上的前端代理,因此浏览器将每个呼叫都视为相同域的请求,并且直接在前端源上设置cookie。该解决方案的缺点包括潜在的性能问题,因为另一台服务器介于两者之间(延迟),并且需要在两个来源上设置cookie(直接访问后端时登录两次)。可以使用nginx,apache或通过http-proxy-middleware在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);
Run Code Online (Sandbox Code Playgroud)