Safari未使用JS Fetch API设置CORS Cookie [英] Safari not setting CORS cookies using JS Fetch API

查看:112
本文介绍了Safari未使用JS Fetch API设置CORS Cookie的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用Fetch API时(实际上,通过Set-Cookie github.com/github/fetch rel = noreferrer>获取polyfill )。相同的代码在FF和Chrome中正常工作(我使用本地填充和polyfill fetch 进行了测试)。

I am unable to get Safari to successfully apply Set-Cookie from server responses when using the Fetch API (actually, via the fetch polyfill). The same code works correctly in FF and Chrome (I tested using both native and polyfill fetch).


  1. 请求是跨域的;

  2. 是的,我正在设置凭据:true ;

  3. 服务器的确响应了 Set-Cookie 标头;

  4. 随后的请求从Chrome和FF发送并带有Cookie请求标头,但Safari没有;

  5. 该请求使用HTTPS(证书是自签名的,并且在开发域中,但是Safari似乎在常规请求中接受了该证书);和

  1. The request is across domains;
  2. yes, I am setting credentials: true;
  3. the server does respond with a Set-Cookie header;
  4. subsequent requests are sent from Chrome and FF with cookie request headers, but Safari does not;
  5. the request uses HTTPS (the cert is self-signed and on a development domain but it seems to be accepted by Safari on regular requests); and

有人知道问题出在哪里吗?

Does someone know what the problem might be?

请仔细阅读文档,并仔细阅读许多已关闭的错误报告。除非我错过了一些东西,否则我认为问题可能出在默认浏览器行为 处理中使用Cookie和CORS-而不是通过抓取(通过polyfill源代码读取,似乎100%不了解Cookie)。一些错误报告表明,错误的服务器响应可能会阻止Cookie的保存。

I've read through the documentation and gone through many of the closed bug reports. Unless I missed something, I think maybe the problem is with the 'default browser behaviour' dealing with cookies and CORS -- and not with fetch (reading through the polyfill source code, it seems 100% ignorant of cookies). A few bug reports suggest a malformed server response can prevent cookies from being saved.

我的代码如下:

function buildFetch(url, init={}) {
    let headers = Object.assign({}, init.headers || {}, {'Content-Type': 'application/json'});
    let params = Object.assign({}, init, { credentials: 'include', headers });

    return fetch(`${baseUrl}${url}`, params);
}

buildFetch('/remote/connect', {method: 'PUT', body: JSON.stringify({ code })})
.then(response => response.json())
.then(/* complete authentication */)

实际的授权请求如下。我正在使用cURL来获取确切的请求/响应数据,因为Safari使其难以复制/粘贴。

The actual authorization request is below. I am using cURL to get the exact request/response data, since Safari makes it hard to copy/paste it.

curl 'https://mydevserver:8443/api/v1/remote/connect' \
-v \
-XPUT \
-H 'Content-Type: application/json' \
-H 'Referer: http://localhost:3002/' \
-H 'Origin: http://localhost:3002' \
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/602.4.8 (KHTML, like Gecko) Version/10.0.3 Safari/602.4.8' \
--data-binary '{"token":"value"}'


*   Trying 127.0.0.1...
* Connected to mydevserver (127.0.0.1) port 8443 (#0)
* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
* Server certificate: mydevserver
> PUT /api/v1/remote/connect HTTP/1.1
> Host: mydevserver:8443
> Accept: */*
> Content-Type: application/json
> Referer: http://localhost:3002/
> Origin: http://localhost:3002
> User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/602.4.8 (KHTML, like Gecko) Version/10.0.3 Safari/602.4.8
> Content-Length: 15
> 
* upload completely sent off: 15 out of 15 bytes
< HTTP/1.1 200 OK
< Access-Control-Allow-Origin: http://localhost:3002
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Api-Key, Device-Key
< Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
< Access-Control-Expose-Headers: Date
< Content-Type: application/json; charset=utf-8
< Content-Length: 37
< Set-Cookie: express:sess=[SESSIONKEY]=; path=/; expires=Fri, 17 Feb 2017 15:30:01 GMT; secure; httponly
< Set-Cookie: express:sess.sig=[SIGNATURE]; path=/; expires=Fri, 17 Feb 2017 15:30:01 GMT; secure; httponly
< Date: Fri, 17 Feb 2017 14:30:01 GMT
< Connection: keep-alive
< 
* Connection #0 to host mydevserver left intact
{"some":"normal","response":"payload"}


推荐答案

回答我自己的问题。

我觉得这很让人生气是Safari的按预期工作行为,尽管我了解它们的动机。 XHR(以及本地获取时可能是本地获取)根本不支持第三方cookie的设置。此故障是完全透明的,因为它是由脚本上下文之外的浏览器处理的,因此基于客户端的解决方案实际上是不可能的。

I find it pretty enraging that this is a "working as intended" behaviour of Safari, though I understand their motivation. XHR (and presumably native fetch when it lands natively) does not support the setting of third-party cookies at all. This failure is completely transparent because it is handled by the browser outside of the scripting context, so client-based solutions are not really going to be possible.

一个推荐的解决方案是在这里可以找到打开API服务器上的HTML页面的窗口或iframe,并在其中设置Cookie。此时,第三方Cookie将开始起作用。这是非常丑陋的,并且不能保证Safari不会在某个时候消除漏洞。

One recommended solution you will find here is to open a window or iframe to an HTML page on the API server and set a cookie there. At this point, 3rd party cookies will begin to work. This is pretty fugly and there is no guarantee that Safari won't at some point close that loophole.

我的解决方案是基本上重新实现执行会话的身份验证系统,饼干呢。即:

My solution is to basically reimplement an authentication system that does what session-cookies do. Namely:


  1. 添加新标题, X-Auth:[令牌] ,其中 [token] 是一个很小的,短暂的JWT,其中包含您会话所需的信息(理想情况下仅是用户ID,这在操作期间不太可能发生变化)应用程序的生命周期-但如果在会话期间可以更改权限,则绝对不会类似于权限);

  2. 添加 X-Auth Access-Control-Allow-Headers ;

  3. 登录期间,设置会话cookie和auth令牌以及有效载荷require(Safari和非Safari用户都将获得cookie和auth标头);

  4. 在客户端上,查找 X-Token 响应标头,并在每次看到它时将其作为 X-Token 请求标头回显(您可以通过使用本地存储来实现持久性-令牌过期,因此,即使该价值存在多年,也无法在特定时间点后赎回);

  5. 在服务器上,对于所有受保护资源的请求,检查cookie并使用它(如果它存在);

  6. 否则(如果cookie不存在,因为Safari并未发送),查找标头令牌,验证并解码令牌有效载荷,使用提供的信息更新当前会话,然后生成新的身份验证令牌并将其添加到响应标头中;

  7. 按正常方式进行。

  1. Add a new header, X-Auth: [token], where [token] is a very small, short-lived JWT containing the information you require for your session (ideally only the user id -- something that is unlikely to mutate during the lifetime of your application -- but definitely not something like permissions if permissions can be changed during the session);
  2. Add X-Auth to Access-Control-Allow-Headers;
  3. During sign-in, set the session cookie and the auth token with the payloads you require (both Safari and non-Safari users will get both the cookie and the auth header);
  4. On the client, look for the X-Token response header and echo it back as an X-Token request header any time it sees it (you could achieve persistence by using local storage -- the token expires, so even if the value lives for years, it can't be redeemed after a certain point);
  5. On the server, for all requests for protected resources, check for the cookie and use it if it exists;
  6. Otherwise (if the cookie is absent -- because Safari didn't send it), look for the header token, verify and decode the token payload, update the current session with the provided info and then generate a new auth token and add it to the response headers;
  7. Proceed as normally.

请注意,JWT(或其他类似方法)旨在解决一个完全不同的问题,因此切勿使用由于存在重播问题,因此用于会话管理(请考虑一下,如果用户打开了两个具有各自标题状态的窗口,将会发生什么情况)。但是,在这种情况下,它们可以提供您通常所需的瞬态性和安全性。最重要的是,您应该在支持cookie的浏览器上使用cookie,使会话信息尽可能的小,使JWT的生存时间尽可能短,并构建服务器应用程序以防止意外和恶意重放攻击。

Note that JWT (or anything similar) is intended to solve a completely different problem and should really never be used for session management because of the "replay" problem (think what could happen if a user had two windows open with their own header-state). In this case, however, they offer the transience and security you normally need. Bottom line is you should use cookies on browsers that support them, keep the session information as tiny as possible, keep your JWT as short-lived as possible, and build your server app to expect both accidental and malicious replay attacks.

这篇关于Safari未使用JS Fetch API设置CORS Cookie的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆