HXWfromDJTU/blog

cookie 系列 - 你的 Token 还好吗?浏览器 SameSite 的 breaking change 应对办法

HXWfromDJTU opened this issue · 0 comments

前言

基于上一篇cookie的基本使用场景和用法,这回来记录下项目关于cookie中遇到的问题和解决方案。

cookie解决的基本问题是http的无状态性,项目中大多数的场景也利用这个特性用于标记用户。

  • 登录后下发auth token标明用户的登录态。
  • 广告商下发ssid token,并通过整个广告联盟中的网站中上报的ssid token,收集用户的访问行为。

Cookie 与 Session

简单滴说,session 是什么呢?

  • Session是一个临时的进程组群,目的是去完成一些任务,是直接存储在内存中的。
  • 每当OS接收到新任务的时候,就会调度进程去执行任务。
  • 一个进程忙不过来就会继续调用其他进程或者创建子进程一起去执行,出现的这些进程会被分为一个个的进程组,最后这些共同完成任务的进程组一起可以被理解为一个Session会话。
  • Session 一般有效期设置为20min,若超时时间内没有数据交互,服务器就会将Session对应的资源删除。

传统 Session/Cookie 工作过程

  1. 当客户端首次向服务端请求的时候,服务端维护一个Session,生成一个sessionid,值可以随意约定,就是一个用于标记session的值。

  2. server 通过 http 响应客户端,客户端接收到了并保存在Cookie中。

  3. 客户端结束掉了请求连接,服务端就会释放掉这些进程对资源的占用,本次会话的状态会被暂时保存在一个文件中,在一定的时间内,服务端会保留这个文件。

  4. 在超时时间范围内,客户端每次访问该域名都会用cookie携带sessionid到服务端。服务端再根据sessionid找对应的Session,并且根据这个状态中记录的内容,打开对应的资源。

存在的问题

  • 客户端有可能禁用cookie
  • 服务端使用内存存储用户信息,当用户数量增大的时候,内存肯定不够用
  • 当服务端采用分布式部署的时候,用户的登录态 Session 并不能够被共享
  • 口令仍然保存在客户端,有可能被仿造 和 存在被盗用的风险

我们来逐个解决下这些问题。

使用 url query 代替 cookie 传参
  • 在用户禁用cookie的情况下,用户访问服务器页面时,服务器可以下发token到前端,前端使用cookie以外的 web storage 技术进行 token 存储。
  • 前端请求后端接口时,从本地存储中取出 token 携带在请求参数中
    • GET 请求拼接在 url query 中
    • POST 请求可以放在 body 中
Session 与 高速缓存
  • 当内存存储出现性能问题、并且会遇到分布式Session不能共享的问题时,高速缓存工具就是首选,比较热门的有 Redis Memcached
  • 缓存服务 与 引用服务将会保持长链接,而并非是频繁的短连接
  • 缓存服务 与 应用服务一般部署在同一个机房,访问速度受到网络影响一般比较小
token 的结构与加密
  • 添加客户端独有的信息作为加密的盐,比如用户访问时的IP或者浏览器型号等等。
  • 明文信息 = 随机生成字符串 + 客户端独有信息后,通过服务端独有的私钥进行加密,最终生成下发的token
  • JWT 结构一般分为三个部分,使用.号分割
    • Header.Payload.Signature
    • Header 结构一般为
        {
          "alg": "HS256",
          "typ": "JWT"
        }
    • Payload 部分则为需要保密的主要内容
      {
          "user_id":"8192qhgb6kmoh3bypbc9wp146jusho",
          "session_id":"81928yaqk1sccfgwze4k718338z3ae",
          "platform":"wechat",
          "roles":"",
          "props": {"botId":"850444981"},
          "exp":1606291794,
          "iat":1603699794
      }
    • Signature 部分则是使用秘钥对前面二者的签名结果,私钥保存在服务器,以下是生成秘钥的示例。
      HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)

第三方cookie 与 隐私

《浏览器原理 - 缓存之cookie》 这篇文章中聊到SameSite时提到了,欧美国家因为隐私安全问题,对Google涉及使用第三方Cookie来追踪用户行为的操作,进行了巨额的罚金。

  1. 各大浏览器厂商也陆续跟进了SameSite属性的实现,将Lax规则设置为默认规则。
  2. 使得网站以前下发的token,在跨站条件下直接无法发送。

什么是第三方cookie

某个 cookie 对应的 domain 值,和当前页面服务器所在的 doamin 不属于一个站点。那么对于这个站点来说,这个 cookie 就是一个第三方cookie

若上图,在淘宝主页面下,就存在hps.tanx.comg.alicdn.com两个第三方cookie。分别是阿里旗下的阿里妈妈营销平台阿里CDN

如何限制第三方coookie

  1. 在旧版(Chrome 80之前)的Chrome浏览器中,默认只在无痕窗口中禁用第三方cookie。

  2. 通过设置SameSite字段
    2020年秋天,SameSite 默认值改为Lax已经在逐步推广,这意味着除了超链接 pre-fetch 之外,所有的跨站请求都不再携带cookie。

SameSite 有多大影响

说了这么多,像是欧美人和几家浏览器巨头在做商业利益和人权之间的权衡,远远影响不到我们。可事实真是这样吗?

  • 广告营销
    从上面阿里妈妈数据平台的第三方 cookie 看出,用户行为收集,数据分析,广告精准投放已经成为大多数平台类的主要收入。而他们标记用户的主要手段就是,在目标网站插入用户信息的脚本。

  • 前端打点上报
    与上述问题相同,使用过Google Analysis的童鞋应该知道,原理其实和广告追踪一样。更像是一种正义的数据收集 [滑稽脸.png]

  • 第三方登录受影响
    许多使用iframe嵌入第三方域的授权登录都将因为之前没有设置SameSite这个字段,而被浏览器升级导致默认为Lax,进而导致之前的Cookie而失效。 (抱歉,👇这个图我又用了一次)

    作为下发授权的网站,需要默认更新你的设置cookie策略,声明式地将SameSite设置为None

  • 非同站的跨域请求

    1. 日常开发中,假如我们的项目名为queen,那么前端通常页面部署在my.queen.io,api的域名通常为api.queen.iologin.queen.io,这种情况不受到SameSite的影响。
    2. 项目多了,需要调用第三方api,调用兄弟部门的api, 则日常的CORS请求中携带对方域名的cookie就会出现问题,不仅仅是withCredentials = true可以解决问题的。
    3. 解决办法参考上一条,需要第三方下发 cookie 时带上SameSite=None

None 必须是 Secure

第三方选择显式关闭 SameSite 属性,将其设为 None时,前提是必须同时设置 Secure 属性, 标明 Cookie 只能通过 HTTPS 协议发送,否则无效。

参考文章

[1] JSON Web Token 入门教程
[2] 当浏览器全面禁用三方 Cookie - 知乎 by conard
[3] Cookie 的 SameSite 属性
[4] SameSite cookies - MDN