Cookie 在 JavaScript(和开发工具)中不可访问,但与 XHR 请求一起发送(不使用 httponly)

Cookies are not accessible within JavaScript (and the dev tools) but sent along with XHR request (no httponly used)

我在具有 session-based 授权的不同域上同时使用 front-end 和 back-end 应用程序。我已经设置了一个有效的 CORS 配置,它在 localhost 上按预期工作(例如,从端口 :9000 到端口 :8080)。一旦我在安全域上部署应用程序(两个域都只允许 HTTPS),CSRF cookie 就无法在 JavaScript 中访问,导致 front-end 的错误 follow-up 请求(缺少 CSRF header).

Cookie 由 Set-Cookie header 中的 back-end 设置,没有 使用 HttpOnly 标志。它实际上是在浏览器的某个地方设置的,因为 follow-up 请求同时包含 session cookie 和 CSRF cookie。尝试通过 JavaScript 访问它(例如在控制台中使用 document.cookie)returns 一个空字符串。 Chrome 的 DevTools 不在 front-end 域上显示 任何 cookie(甚至没有列出 back-end 域)。

我希望设置 cookie 并在当前域(front-end 域)上可见。我正在使用 axios 库的 withCredentials 标志。

您知道为什么无法从 JavaScript 或 Chrome 中的 DevTools 访问 cookie 吗?这跟Strict-Transport-Securityheader有关系吗?


Headers

1.初始 GET 响应 Header

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。 Follow-up POST 请求 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 a CSRF header which would automatically be added if the cookie was accessible with JavaScript.

如您所见here,XHR 规范明确禁止读取Set-Cookie。最好的方法是在 header 而不是 cookie 中传递信息。

1

您可能需要添加 Access-Control-Allow-Headers header 以允许传递特定的 header。

请尝试将以下内容添加到您的服务器响应 headers(OPTIONS 方法)以进行测试

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

在生产中,我建议限制 headers 如下(但我不是 100% 确定 header 列表是否正确,如果可行,需要在这里进行试验)

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

参考这个https://quickleft.com/blog/cookies-with-my-cors/

2

您可能遇到的另一个问题是,您 cookie 将设置在您的后端服务所在的域上(而不是在您查询的域上)

请也检查这个

3

作为最后一个问题的选项 - 浏览器可以禁止从来自 a.xxx.com

的请求中为域 b.xxx.com 设置 cookie

在这种情况下,您可以尝试在 parent 域 xxx.com 上设置 cookie,这样它就可以用于您的客户端

TL;DR: Read-access 到 cross-domain cookies 是不可能的。将 CSRF 令牌添加到响应 header 将是一个解决方案。完全规避 CORS 和 cross-domain 请求的另一种解决方案是使用反向代理。


问题

正如我在上面的问题中所述,我的 front-end 的 JavaScript 部分(例如 https://example1.com 正试图从我的 back-end 例如 https://example2.com。为了能够使用 JavaScript 访问远程 API,我正在使用 CORS。这允许请求通过。我正在使用 [= front-end 端的 14=] 和 back-end 端的 Access-Control-Allow-Credentials: true。然后 Set-Cookie header 将 cookie 设置在 back-end 源和不在 front-end 来源。因此,cookie 在 DevTools 和 JavaScript.

中的 document.cookie 命令中都不可见

在 back-end 源上设置的 Cookie 始终是通过 CORS 向 back-end 请求的一部分。但是,我需要访问 CSRF cookie 的 content 以将令牌添加到请求 header 中(以防止 CSRF 攻击)。正如我发现的那样,无论使用什么 CORS 设置,都无法使用 JavaScript 从不同的域读取(或写入)cookie(请参阅这些 Whosebug 答案:[1], [2])。浏览器将对 cookie 内容的访问限制为 same-domain 个来源。

解决方案

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

另一种解决方法是使用反向代理,它完全避免了 CORS 和 cross-domain 请求的问题。使用这样的反向代理在 front-end 上提供了一条特殊路径,该路径将被重定向到 back-end (server-side)。例如,对 https://front-end/api 的调用被代理到 https://back-end/api。因为来自 front-end 的所有请求都是向同一域上的 front-end 代理发出的,所以浏览器将每次调用都视为 same-domain 请求,并且直接在 front-end 上设置 cookie起源。该解决方案的缺点包括潜在的性能问题,因为另一台服务器正在 in-between(延迟)并且需要在两个来源上设置 cookie(直接访问 back-end 时需要登录两次)。可以使用 nginx、apache 或使用 Node.js 中的 http-proxy-middleware:

轻松设置反向代理

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);

总之无法访问跨域cookie,document.cookie只能访问当前(or parent)域cookie

ssc-hrep3 在他的问题中提到 "both domains" 是根本原因的暗示。

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

有关详细信息和解决方法,请参阅