Chrome 51 开始,浏览器的 Cookie 新增加了一个SameSite
属性,主要用于防止CSRF攻击和用户追踪。
为什么这时候来讲这个事情呢,在Chrome 80之后,由于SameSite
默认值的改变,导致大部分浏览器在跳转跨站的网站时没有携带Cookie
,造成登录态失效等一系列问题。
为了保护保护用户隐私,Safari更加直接,默认直接禁用了第三方携带Cookie
。导致链接跳转或者接口请求第三方都无法携带Cookie
。最直观的就是,比如,当前网页有一个 GitHub 链接,用户点击跳转就不会带有 GitHub 的 Cookie
,跳转过去总是未登陆状态。
在分析SameSite
之前,我们有必要先来了解一下跨站和跨域的区别。
说到浏览器的跨站和跨域,个人觉得很多人容易混淆。虽然一字之差,但是不管是概念还是作用上,都有蛮大的区别的。接下来我们来一起学习以下。
跨域
跨域对应的是同域,同域即是浏览器的同源策略,这里不单单是指相同域名,域名、协议、端口号必须完全一致,才属于同源。只要其中一个不相同,都是属于跨域了。
跨域的解决方案有很多,可以在其他渠道学习下,比较常用的是JSONP
、CORS
、new Image
等。
跨域主要是针对请求的。如果跨域,浏览器会在控制台提示类似的Error
。
上面导致跨域的原因是端口号不一致。
跨站
跨站对应的是同站,也可以称呼叫做第一方(同站)或者第三方(跨站)。同源策略作为浏览器的安全基石,其「同源」判断是比较严格的。相对而言,Cookie
中的「跨站」就相对宽松一些,只要协议相同,并且有效顶级域名+二级域名相同即可,不用考虑端口号。文字比较晦涩,讲个例子就简单很多了。
同站:zhuanlan.zhihu.com
和zhihu.com
。.com
属于顶级域名,而.zhihu
属于二级域名。这里属于同站,但是跨域了。
跨站: zhihu.com
和baidu.com
、a.github.io
和b.github.io
。第一个例子虽然顶级域名相同,但是二级域名不一样了。第二个例子看起来会觉得奇怪,这里github.io
属于顶级域名,a
和b
才是二级域名,所以这里也跨站了。
再比如,http://a.zhihu.com
和https://zhuanlan.zhihu.com
,虽然顶级域名和二级域名相同zhihu.com
,但是因为他们的协议不同,也属于跨站。
看完例子后,可以给个简单的结论:跨站一定跨域,跨域不一定跨站。
SameSite
简单的讲完跨域和跨站之后,我们来看下这边文章的主题部分。
开头就有提到,SameSite
属性可以让 Cookie 在跨站请求时不会被发送,从而阻止了跨站请求伪造攻击(CSRF)。它有三个属性值
- Strict 完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie。
- Lax 允许部分第三方请求携带 Cookie。
- None 无论是否跨站都会发送 Cookie。
Chrome的SameSite默认值是Lax,而Safari的默认值是Strict。
可以看下github
关于SameSite
的处理。
大部分的Cookie
都是浏览器的默认值Lax
,当然还有 Strict
这种严格禁用第三方Cookie
发送的,还有个None
,即无论是否跨站都会发送该Cookie
。
以前呢,SameSite
的默认值是None
, 即当前页面嵌入其他第三方页面(iframe)、POST请求、Ajax都可以携带第三方的Cookie
。但是默认值调整了之后,会产生一些影响,我们先来看看默认值改变之后,Cookie
的发送情况
从上图可以看出,对大部分 web 应用而言,POST 表单,iframe,AJAX,Image 这四种情况从以前的跨站会发送三方 Cookie
,变成了不发送。
POST表单和AJAX请求在跨站请求的时候不发送Cookie
是ok的, 这样可以有效阻止跨站请求伪造攻击。
Image不发送Cookie
其实影响比较小,因为大部分静态资源都会放在CDN上, 不随业务变化,可以实现强缓存。
而iframe一般都是跨站的,所以受影响相对较大。
解决
方法一:
可以将SameSite
设置为None,但是这里需要注意。
- SameSite=none 只支持HTTPS接口。如果要设置该值,需要在对应的
Cookie
上同时设置Secure
属性。 - 部分浏览器不支持部分SameSite=none。IOS 12 的 Safari 以及老版本的一些 Chrome 会把 SameSite=none 识别成 SameSite=Strict,所以服务端必须在下发 Set-Cookie 响应头时进行 User-Agent 检测,对这些浏览器不下发 SameSite=none 属性
方法二:
既然浏览器针对SameSite
的默认值实现不一致,在跨站的时候会部分或者完全禁用携带第三方Cookie
,那我们在跨站请求接口或者链接跳转的时候,比如 jd.com
和jinrong.com
,这样处理呢(滑稽)
- 在
jd.com
页面发生请求或者跳转时,生成唯一标识的值。将Cookie
和唯一标识存储到服务器。 - 在进入到
jinrong.com
页面或者相关接口时,提前根据唯一标示,获取到对应的Cookie
set到浏览器,再处理业务逻辑。 - 最后就可以实现跨站携带
Cookie
了。
大概就是将跨站前将Cookie
存到数据库,跨站后将Cookie
set到浏览器,再做其他一系列业务逻辑。
总结
- 跨站一定跨域,跨域不一定跨站
- 只要有效顶级域名+二级域名不相同,就属于跨站
SameSite
的作用是防止跨站请求伪造(CSRF)攻击和保护用户隐私- Chrome的
SameSite
默认值是Lax
,而Safari的SameSite
默认值是Strict