日前 Chrome 工程师 Mike West 发表了一篇文章提议改造 Cookie 标准,以强化 HTTP 状态管理。
Mike 分析了目前 Cookie 存在的几个方面的问题,包括很难安全使用、浪费用户资源,以及隐私问题,通过它可以以令人惊讶的方式跟踪用户在网络上的活动。
关于浪费用户资源,Mike 解释,服务器可以为一个注册域名存储大量 Cookie,并且很多 Cookie 可以通过 HTTP 请求发送。例如 Chrome 允许为每一个域名存储大约 180 个 Cookie,相当于约 724kB 数据。在众多 Cookie 中,其请求头大小的中位数是 409 字节,但是其中却有 90% 有 1589 字节,95% 占了 2549 字节,99% 甚至达到了 4601 字节,另外有约 0.1% 的 Cookie 头非常大,超过了 10kB。如此滥用,效率低下。
隐私方面,众所周知 Cookie 可以用于身份验证,但它同时也可以用来悄悄跟踪用户的相关信息。
而关于安全使用的难处,Mike 列出了几条在开发中安全使用 Cookie 遇到的问题:
- Cookie 对 JavaScript 默认是可用的,这使得一次 XSS 可以获取持久凭证。虽然十年前引入了 HttpOnly 属性,目前也只有大概 8.31% 的人使用 Set-Cookie 进行相应设置。
- 默认情况下,Cookie 会被发送到非安全的源,这会导致凭据被盗。Secure 属性虽然可以标记安全的 Cookie 源,但目前只有大概 7.85% 的人使用 Set-Cookie 进行了设置。
- Cookie 经常在请求发送者毫不知情的情况下被发送。SameSite 属性可以减少 CSRF 风险,但是目前只有大概 0.06% 的人使用 Set-Cookie 进行了设置。
Mike 认为,一方面 Cookie 采用的缓解安全问题的属性很差,Cookie 根本不符合我们决定对其它类型的 Web 可访问数据强制执行的安全边界。它们在给定的可注册域中流过源,它们忽略端口和方案,这意味着它们可以被网络攻击者轻易伪造,并且它们可以缩小到特定路径,这些特征使得它们难以推理,并制定激励措施来削弱平台其它部分的同源策略。
Mike 给出了一套新方案,他解释,用户代理可以通过为用户访问的每个安全源生成唯一的 256 位值来控制它代表用户表示的 HTTP 状态,此 Token 可以作为结构化 HTTP 请求头传递到源:
Sec-HTTP-State:token = * J6BRKagRIECKdpbDLxtlNzmjKo8MXTjyMomIwMFMonM *
此标识符或多或少类似于客户端控制的 Cookie,但有一些值得注意的区别:
- 客户端控制 Token 的值,而不是服务器。
- Token 只能用于网络层,而不能用于 JavaScript(包括类似网络的 JavaScript、例如 Service Workers)。
- 用户代理每个源只生成一个 256 位 Token,并且只将 Token 暴露给生成它的源。
- 不会为非安全源生成或传递 Token。
- 默认情况下,Token 将与同一站点请求一起提供。
- Token 一直存在,直到服务器、用户或用户代理重置为止。
在些基础上,将为开发人员提供一些可通过 Sec-HTTP-State-Options HTTP 响应头触发的控制点,有如下选项:
1、某些服务器需要跨站点访问其 Token,其它服务器可能希望将交付范围缩小到同源请求,服务器可以指定任一选项:
Sec-HTTP-State-Options: ..., delivery=cross-site, ...
或者:
Sec-HTTP-State-Options: ..., delivery=same-origin, ...
2、某些服务器希望限制 Token 的生命周期,可以允许它们设置 TTL(以秒为单位):
Sec-HTTP-State-Options: ..., ttl=3600, ...
时间到期后,Token 的值将自动重置。同时服务器也可能希望明确触发 Token 的重置行为(例如,在注销时),这可以通过设置 TTL 为 0 来实现:
Sec-HTTP-State-Options: ..., ttl=0, ...
在任何一种情况下,都可以向当前运行的页面通知用户的状态变化,以便执行清理操作。当发生重置时,用户代理可以将消息发送到名为 http-state-reset 的源的 BroadcastChannel(并且可能唤醒源的 Service Worker 以响应用户驱动的重置):
let resetChannel = new BroadcastChannel('http-state-reset')); resetChannel.onmessage = e => { /* Do exciting cleanup here. */ };
3、对于某些服务器,客户端生成的 Token 足以维持状态,它们可以将其视为不透明的会话标识符,并将用户的状态绑定到服务器端。其它服务器需要额外的保证,他们可以信任 Token 的出处,为此,服务器可以生成唯一密钥,将其与服务器上的会话标识符相关联,并通过 HTTP 响应头将其传递给客户端:
Sec-HTTP-State-Options: ..., key=*ZH0GxtBMWA...nJudhZ8dtz*, ...
客户端将存储该密钥,并使用它来生成某些数据集的签名,从而降低 Token 被捕获的风险:
Sec-HTTP-State: token=*J6BRKa...MonM*, sig=*(HMAC-SHA265(key, token+metadata))*
Mike 同时也表示,该方案并不是一个完全与 Cookie 不同的新东西,并不是要在目前替换掉 Cookie,虽然弃用 Cookie 是应该的,但是当下该方案只是提出了一种在 Cookie 同时存在的情况下也能发挥作用的补充机制。
转自 https://www.oschina.net/news/99542/tightening-http-state-management