我正在使用基于会话的授权在不同的域上使用前端和后端应用程序。我已经设置了一个有效的CORS配置,它在localhost
上按预期工作(例如从端口:9000
到端口:8080
)。一旦我在安全域上部署应用程序(两个域都只允许HTTPS),在JavaScript中就不再可以访问CSRF cookie,从而导致前端的错误后续请求(缺少CSRF头)。
使用Set-Cookie
标记,HttpOnly
标题中没有的后端设置Cookie。它实际上设置在浏览器的某个位置,因为后续请求包含会话cookie和CSRF cookie。尝试通过JavaScript访问它(在控制台中使用例如document.cookie
)返回一个空字符串。 Chrome的DevTools不会在前端域上显示任何 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
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]
此请求应包含CSRF标头,如果可以使用JavaScript访问cookie,则会自动添加该标头。
答案 0 :(得分:4)
您可能需要添加Access-Control-Allow-Headers标头以允许传递特定标头。
请尝试将以下内容添加到服务器响应标头(OPTIONS方法)中以进行测试
Access-Control-Allow-Headers: Content-Type, *
在制作中,我建议限制标题如下(但我不能100%确定正确的标题列表,如果有效,需要在此进行实验)
Access-Control-Allow-Headers: Cookie, Set-Cookie
请参阅此参考资料https://quickleft.com/blog/cookies-with-my-cors/
您可能遇到的另一个问题是, Cookie将设置在您的后端服务所在的域(不在您查询的域中)
请检查一下
作为上一个问题的一个选项 - 浏览器可以禁止为来自b.xxx.com
a.xxx.com
的Cookie
在这种情况下,您可以尝试在父域xxx.com
上设置Cookie,以便它可供您的客户端使用
答案 1 :(得分:4)
TL; DR :无法对跨域Cookie进行读取访问。将CSRF令牌添加到响应头是一种解决方案。另一种完全绕过CORS& amp;跨域请求将使用反向代理。
正如我在上面的问题中所述,我的前端的JavaScript部分(例如https://example1.com
)正试图从我的后端访问非HttpOnly
Cookie,例如https://example2.com
为了能够使用JavaScript访问远程API,我正在使用CORS。这允许请求通过。我在前端使用withCredentials: true
,在后端使用Access-Control-Allow-Credentials: true
-end side。Set-Cookie
标题然后在后端原点而不是前端原点设置cookie。因此,cookie在DevTools和document.cookie
命令中都不可见。的JavaScript。
设置在后端源的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(直接访问后端时登录两次)。通过在Node.js中使用http-proxy-middleware
,可以使用nginx,apache或非常简单地设置反向代理:
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 :(得分:2)
简而言之,无法访问跨源Cookie,document.cookie
只能访问当前(or parent)域Cookie。
这是造成根本原因的暗示,是ssc-hrep3在他的问题中提到“两个领域”。
从localhost部署切换到后端服务器和前端服务器的不同端口,再使用两个不同的主机时,很容易犯这个错误。这将在本地工作,因为cookie在端口之间共享,并且在使用两个不同的主机时将失败。 (与其他一些也将在本地公开的CORS问题不同)
有关详细信息和解决方法,请参阅ssc-hrep3's answer。
答案 3 :(得分:1)
正如您可以阅读here,XHR规范明确禁止阅读Set-Cookie。最好的方法是在标题而不是cookie中传递信息。