那些年踩过cookie的那些坑

561 阅读7分钟

前言

最近烧烤哥又又又踩了cookie的一个坑,虽然最后发现是兄弟项目搞的事情,但是,终究发现,自己对cookie的坑的认知还没有完全拉满,所以想总结一下这些年踩过的cookie的坑。

哪里有坑

cookie无非就是网站存在浏览器的一个数据,每当我们打开这个网站或者是向指定的网站发送请求时会携带这个该网站的cookie,用来维持网站的会话。

一般来说,单一站点的cookie只关注存取,顶多我们注意一下安全相关的(像设置http-only预防xss攻击)就没有什么问题了。可是,随着互联网世界越来越丰富和用户场景越发复杂,用户数据跨站点共享的场景也越来越平常了,同时,无论是用户还是政府组织机构,对于隐私的的敏感性也越发增强。所以,平时,不需要我们怎么处理的cookie,在复杂的系统下也会频发问题。

SSO 单点登录系统

烧烤哥开发维护的系统就是一个业界比较常见的用户系统解决方案——SSO单点登录系统。这种方案说简单也不简单,它可以有很多实现方法,最终的目标将单点登录的系统的用户登录令牌传递给业务系统,下面我简单用一张时序图来介绍一下我们当前使用的单点登录系统:

test.png

从图可以看到,业务系统my.company.com和单点登录系统id.company.com是同域的,那么为什么我们还要那么麻烦自己去维护自己的token呢(id.company.com写的token是位于根域名下,my.company.com是可以获取到的)?其中原因是我们的my.company.com的业务经常会去做定制版本,而且会定制域名,这时候就可能叫my.company2.com了,所以为了能在不同的定制域名中获取到这个登录令牌,必须自己维护一套token。

背景大概清晰了,那我们踩过哪些坑呢?

打破cookie携带规则的SameSite属性

This set-cookie didn't specify a "SameSite" attribute and was defaulted to "SameSite=Lax" and broke the same rules specified in the SameSiteLax value

这个问题其实比较大一点的公司都可能遇到,其根因是chrome浏览器在80版本之后将SameSite的默认值设置为Lax了,那意味着什么?存在跨站的网址相互请求的时候设置cookie和携带cookie时如果不设置SameSite将会被禁止。

未命名文件 (5).png

影响范围还不小

这个问题很棘手,基本我们的系统都不同程度受到了影响:

  1. 打开定制域名 my.company2.com 一直转到登录界面
  2. 打开域名时my.company.com 也一直跳转到登录界面

漏说了一点,我们的登录界面并不是id.company.com提供的登录界面,这个登录界面还是我们业务系统的,不过是使用了iframe嵌入id.company.com的登录模块实现的登录(这样的做法是为了自定义漂漂亮亮的登录界面)。 未命名文件 (8).png

问题是出在什么地方?

问题1很好理解,因为定制域名和.company.com跨站了,因为浏览器的更新无法自动携带cookie。

问题2就很难理解了,明明同站,也携带不了?通过调试发现有两个逻辑我们忽略了:

id.company.com会在.company.com中注入一个x-token还有一个x-samesite-none-token x-toke是的Samesite默认的lax,x-samesite-none-token就好理解了,samesite值为none

更重要的一点是,浏览器要求,设置samesite值为none的站点必须是可信协议,那就是https;

所以,问题2的问题在于,登录界面使用id.company.com的站点是http,没有将x-samesite-none-token设置上,然后在非登录界面去调用id.company.com 获取用户信息,又触发了一个知识点:

chrome86之后的同站规则扩大为包含协议在内,以前的同站规则是顶级域名,现在包含了协议

解决方案

最终,为了解决这两个问题,我们做了以下改变:

未命名文件 (7).png

其中最重要的点是保证id.company.com能够正常注入x-samesite-none-token,当产生跨站的时候就可以使用这个令牌去请求用户信息了。

僵尸cookie出现

线上用户反馈,登录我们的系统一直都登录不上去,重复登录还是不行

下意识是不是samesite又搞什么鬼了,让用户清理cookie、localstorrage发现就可以恢复了。

分析问题

如果是samesite的问题,那应该不会恢复才对。远程用户电脑发现,id.company.com明明设置了token A,但是业务请求后台的时候确实token B。再深入发现了猫腻:

cookie名称domain
x-auth-tokenAmy.company.com
x-auth-tokenB.company.com

那就尴尬了,这个B是谁设置的?浏览器为什么也把这个B携带上了?为什么后台要用这个B而不用A,按照 cookie domain的规则,不应该是优先使用A吗?

疑问N连,首先这个B不是我们的站点设置的,打死也不是!经过摸索,发现是另外的兄弟系统设置的。。。(偷偷diss一下,自己的token不挂在自己的域名下,挂在根域名下,太看得起自己了!!)

对比测试

根因找到之后,我们就解决第二个问题,cookie优先级问题。我们先从下面的案例去重新学习一下:

cookie名称domainpath
x-auth-tokenAmy.company.com/
x-auth-tokenBmy.company.com/index
x-auth-tokenC.company.com/
x-auth-tokenD.company.com/index
const { ctx } = this;

ctx.cookies.set('x-auth-token', 'A', {
    domain: 'my.company.com',
    path: '/',
    signed: false,
});
ctx.cookies.set('x-auth-token', 'B', {
    domain: 'my.company.com',
    path: '/index',
    signed: false,
});
ctx.cookies.set('x-auth-token', 'C', {
    domain: '.company.com',
    path: '/',
    signed: false,
});
ctx.cookies.set('x-auth-token', 'D', {
    domain: '.company.com',
    path: '/index',
    signed: false,
});

那这几个具体的携带情况是怎么样的呢?

image.png

我们会发现:B > D > A > C

似乎这结果并没有超出我们的预期,难道说我们理解没有错,匹配度越高的cookie越排前面?

肯定不是,不然不会有bug的,查阅资料发现:

image.png

RFC 6265 提案中提到这个cookie同名时优先级的问题:

  • cookie的path长度越长,优先级越高
  • cookie的path长度一样时,创建时间越早的优先级越高

我上面的案例创建的cookie顺序是从A到D(也就代表着时间),所以这个结果是合理的,下面我们稍微改一下设置cookie的顺序:D -> C -> B -> A, 现象是如下: image.png

上图印证了这个RFC的提案在chrome下是完全遵守的,D和B在同的path长度情况下,因为D更早设置,所以D的优先级更高。

当然,我的理解“cookie的优先级与domain有关”被证明是错误的。

所以,回到我们的大坑,兄弟业务把x-auth-token设置在了公共根域名下,同时这个x-auth-token因为优先级高的问题,一直被我们的业务所优先使用,导致登录失败,且重新登录之后,也无法覆盖。另外一点,我们重新登录之前会登出,登出时会清空我们设置的cookie,但是,我们只清空了my.company.com域下的x-auth-token。

彻底清除僵尸cookie

俗话说,靠人不如靠己,所以,我们需要在登出时清掉僵尸cookie。

修改、删除cookie时,新建的cookie除value、maxAge之外的所有属性,例如name、path、domain等,都要与原cookie完全一样。否则,浏览器将视为两个不同的Cookie不予覆盖,导致修改、删除失败。

基于cookie覆盖的要求,如果出现上述ABCD这四种情况,那就得一一覆盖了。

永久解决方案:不再使用x-auth-token这个cookie名称,可以使用私有性比较高的如 x-${my}-token,这样就不会冲突啦!!

总结

简单的两个案例,诉尽烧烤哥几年的cookie斗争史。在安全性加强和业务愈来愈复杂的现实下,很多人会慢慢抛弃cookie转而使用localstorage,localstorage和cookie的优缺点不过多赘述,烧烤哥信奉 存在即合理 至上逻辑,让时间去过滤不合理的东西。

另外,像这种坑,踩中了其实是一种积累,最终获得了挺多有用的知识。

image.png

最后

觉得烧烤哥写的不错的,点赞关注吧;如有不足的地方,请不吝指正。 如果你也遇到一些奇奇怪怪的cookie的坑,评论区见!