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

标签 javascript cookies cross-domain hsts strict-transport-security

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

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

我希望 cookie 被设置并在当前域(前端域)上可见。我正在使用 axioswithCredentials 标志图书馆。

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


标题

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。后续 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]

This request should contain a CSRF header which would automatically be added if the cookie was accessible with JavaScript.

最佳答案

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


问题

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

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

解决方案

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

另一种解决方法是使用反向代理,它可以完全避免 CORS 和跨域请求的问题。使用这样的反向代理在前端提供了一条特殊路径,该路径将被重定向到后端(服务器端)。例如,对 https://front-end/api 的调用被代理到 https://back-end/api。因为前端的所有请求都是向同域的前端代理发出的,所以浏览器将每次调用都视为同域请求,并直接在前端源上设置cookie。该解决方案的缺点包括潜在的性能问题,因为另一台服务器介于两者之间(延迟),并且需要在两个源上设置 cookie(直接访问后端时登录两次)。可以使用 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);

关于javascript - 在 JavaScript(和开发工具)中无法访问 Cookie,但会与 XHR 请求一起发送(不使用 httponly),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46322346/

相关文章:

javascript - for循环索引如何提升原型(prototype)链?

javascript - 检查 D3 中潜在的 CSV 文件问题

php - 显示 cookie 数组中的 3 个值?

cookies - 我可以使用 <img> 标签跨域发送 cookie 吗?

Javascript 读取嵌套的 Json 项目

javascript - 使用 D3.js 的 3D 条形图的透视问题

javascript - 切换多个复选框 – 原型(prototype) js

node.js - express 中的 req.cookies 未被填充

jquery - 是否可以对 application/json 进行跨域 POST ajax 请求?

javascript - 因此,AngularJS : No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' 不允许访问