如果你最近有关注过chrome的控制台,可能会发现经常报一些warning:
A cookie associated with a cross-site resource at http://baidu.com/ was set without the
SameSiteattribute. A future release of Chrome will only deliver cookies with cross-site requests if they are set withSameSite=NoneandSecure.

出现这个警告的原因是:chrome在80版本之后,更新了cookies的携带机制,把原来Cookie的SameSite属性值,由None改成了Lax,这就会导致一些需要第三方cookie的应用产生了异常。
在介绍SameSite属性之前,我们先来复习一下cookie的基础知识
Cookie基础
Cookie常见的属性:
- Name: cookie名。
- Value: cookie值。
- Domain: cookie的域。如果设成
.deepred.com,那么a.deepred.com和b.deepred.com域名下,都可以使用.deepred.com的cookie。 - Path: cookie的路径。请求资源的路径一定要包含这个path才能携带cookie。一般设置成
/即可。 - Expires/Max-Age: cookie过期时间。默认不设置,则是
Session会话,关闭页面后,该cookie立即失效。 - HttpOnly: 设成
true后,JS使用document.cookie则访问不到。常用于避免XSS攻击。 - Secure: 标记为Secure的cookie只应通过被HTTPS协议加密过的请求发送给服务端。
- SameSite: 用来限制第三方Cookie
最后一个属性非常重要,也就是我们即将要说的SameSite了。
Cookie携带的场景
我们假设有一个名字为sessionId的cookie,domain设置成了.demo.com。
1.在a.demo.com域名下,ajax在该域名下的所有请求,都会自动带上sessionId
ajax.get('/api/data') // 自动带上sessionId
2.在b.demo.com域名下,ajax在该域名下的所有请求,都会自动带上sessionId
ajax.post('/api2/data2') // 自动带上sessionId
3.在b.demo.com域名下,ajax请求a.demo.com的api,需要设置withCredentials才能带上sessionId
ajax.get('https://a.demo.com/api/data') // 不能自动带上sessionId
ajax.get('https://a.demo.com/api/data', {withCredentials: true}) // 自动带上sessionId
注意一下:
https://a.demo.com/api/data需要支持cors跨域,并且Access-Control-Allow-Origin不能设成*,要设置成https://b.demo.com,只有这样,withCredentials才有用
router.get('/api/data', (ctx, next) => {
ctx.set('Access-Control-Allow-Origin', ctx.headers.origin);
ctx.set('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , myheader');
ctx.set('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
ctx.set('Access-Control-Allow-Credentials', 'true');
};
4.在b.demo.com域名下,使用iframe加载a.demo.com,会自动带上sessionId
a.demo.com和b.demo.com同属一个域名下的子域名(同站)
5.在a.demo2.com域名下,ajax请求a.demo.com的api,需要设置withCredentials才能带上sessionId
ajax.get('https://a.demo.com/api/data') // 不能自动带上sessionId
ajax.get('https://a.demo.com/api/data', {withCredentials: true}) // 自动带上sessionId
6.在a.demo2.com域名下,使用iframe加载a.demo.com,会自动带上sessionId
a.demo.com和a.demo2.com属于完全不相干的两个网站(跨站)
目前为止,都是我们所熟知的cookie携带场景。
然而,在chrome 80版本之后,谷歌把cookie的SameSite属性,从None改成了Lax。这时候,会导致第5和第6种场景,由于跨站导致sessionId丢失!
跨站解释
a.demo.com和b.demo.com属于同站,a.demo.com和a.demo2.com属于跨站
注意和跨域做比较: a.demo.com和b.demo.com属于跨域
SameSite
cookie的SameSite属性用来限制第三方Cookie,从而减少安全风险(防止CSRF)
SameSite可以有下面三种值:
Strict仅允许一方请求携带Cookie,即浏览器将只发送相同站点请求的Cookie,即当前网页URL与请求目标URL完全一致。Lax允许部分第三方请求携带CookieNone无论是否跨站都会发送Cookie

从上图可以看出,SameSite从None改成了Lax后,Form,Iframe,Ajax和Image中跨站的请求受到的影响最大。
解决方法
解决方法也很简单粗暴:强行把SameSite设置成None。不过需要特别注意几点:
1.SameSite设置成None后,Cookie就必须同时加上Secure属性
ctx.cookies.set('sessionId', {
maxAge: 1000 * 60 * 60,
secure: true,
sameSite: 'none',
});
这也意味着,你的网站需要支持https!(Lax和Strict不需要支持https)
如果线上的网站同时支持http和https,你可能需要让运维将http强制重定向到https(建议使用307状态码而不是302状态码)
2.部分浏览器不能加SameSite=none,比如IOS 12的Safari,以及一些老版本的chrome浏览器,它们会错误的把SameSite=none识别成SameSite=strict。
具体不兼容的浏览器可以见这里
因此后端要根据UA来判断是否加上SameSite=none