blog
blog copied to clipboard
更换域名的后续问题:SameSite
经过一番折腾,我终于将划词翻译的网站从 hcfy.limingkai.cn 迁移到 hcfy.app 了,本来以为万事大吉了,但测试的时候又发现了一个问题:跨站请求没有携带 cookie,但是在迁移前是正常携带了 cookie 的。
先说一下背景:用户在划词翻译内登录之后,会有一个 cookie 被写进浏览器,cookie 的域跟接口地址是同一个:hs-api.limingkai.cn
。然后,hcfy.limingkai.cn 会通过接口获取用户的信息,在请求接口的时候,浏览器会自动带上 cookie,从而实现了用户认证。
但是,当我把网站地址从 hcfy.limingkai.cn 迁移到 hcfy.app 之后,这个 cookie 没有被浏览器携带上去。
我首先确认了调用 fetch()
时正确设置了 credentials: 'include'
,然后确认在浏览器地址栏里直接打开 hs-api.limingkai.cn
的时候,通过 Chrome Dev Tool 是能看到 cookie 的。这两点都确认无误后,我想到了 SameSite,但是又很疑惑——在迁移前是正常的,应该不会是 SameSite 的问题吧?但现在除了 SameSite 好像也没有别的原因了。
在查阅了相关的文档之后,我知道原因了。
Chrome 默认将 cookie 的 SameSite 设置为了 Lax,即允许同站之间携带跨域 cookie。这个“同站”的概念跟“同源”是不一样的,而这正是此次问题出现的原因:hcfy.limingkai.cn 跟 hs-api.limingkai.cn 是“同站”的,因为它们都有同一个顶级域名 limingkai.cn,但是 hcfy.app 跟 hs-api.limingkai.cn 不是同站的。
这就是迁移前 cookie 能正确携带但迁移后不能的原因。
知道原因了,接下来要想解决方案了。
确保网站跟接口地址“同站”——将 hs-api.limingkai.cn 迁移到 api.hcfy.app
这是我很想做的一件事情,因为既然划词翻译有自己独立的域名了,那么 API 接口地址也应该挂在独立域名下面,但是有两个问题:
- 已经登录过的用户的认证 cookie 是保存在 hs-api.limingkai.cn 这个域下面的,如果切换了接口地址,会导致已经登陆的用户全都退出登录。
- 即使在新版本中改为了对新接口地址的请求,但仍然会有使用旧版本划词翻译的用户继续使用 hs-api.limingkai.cn。我需要同时兼顾两个接口的请求,这还涉及到在同一个服务器上为这两个接口提供 SSL 证书。
更新:迁移失败,未备案的域名 DNS 指向国内服务器是无法访问的。见 #102
修改 SameSite 为 None
当我在 Chrome Dev Tool 将 cookie 的 SameSite 改为 None 之后就能正常携带用户的登录 cookie 了。所以,我只需要在用户登录时给 cookie 多加一个 SameSite=None 就好。
但我遇到了两个问题:
- SameSite=None 确实太宽松了,还得配合 CRSF Token 才能确保万无一失。更新:也可以通过检测 Referer 头来防止。
- 我用的是 Passportjs ,但它似乎不支持给 session cookie 添加 SameSite(至少我在文档和 Google 里都没找到),估计得研究一番。更新:Passportjs 其实是依赖的 koa-session 来设置 cookie 的,所以只需要在 koa-session 里改配置就好了。
不要用 cookie 作认证
解决这个问题还有一个办法就是不要把 session id 写进 cookie,而是直接返回给客户端 js 保存下来,然后每次请求接口的时候就带上。
这样做的好处是:
- 由于不再依赖 cookie,那么就不会有 SameSite 的问题了,网站地址和接口地址不需要满足“同站”的要求了。
- 不用加 CRSF Token 了。
但坏处是:
- 用户在划词翻译里登录之后,打开官网的话就不能自动获取到登录状态了。
- 改造成本略高,而且我还需要同时兼顾使用 cookie 认证的方式,因为扩展程序不同于网站,还是有很大一部分用户不会及时升级到最新版本的。
结论
能最快速解决问题的办法是给目前的 cookie 加上 SameSite=None。这应该不难做到,理论上只需要在 Node.js 发送响应前检查一下有没有设置 session id,有的话就补上 SameSite 即可—— Koajs 的洋葱模型好像可以派上用场了。
但综合来看,还是把 hs-api.limingkai.cn 迁移到 api.hcfy.app 更好:扩展程序和网站能共享登录状态、不用加 CRSF Token、划词翻译的接口地址跟 limingka.cn 彻底脱钩。
查阅过的文章: