微信号:ArchNotes

介绍:高可用架构公众号.

Cookie或将被替换!Chrome工程师提议新型HTTP状态管理协议

2018-08-29 11:25 Mike Wes

问题


Cookie允许无状态的HTTP协议支持有状态会话,在web上,我们依靠Cookie实现了很多有趣的功能。即便如此,Cookie依然还是有很多问题:使用起来不够安全,浪费资源,使用一种令人惊讶的方式追踪用户在网络上的活动。

安全:这些年我们引入过很多的特性,试图提供合理的安全属性给那些关心安全的开发者,但也只是降低了安全问题而已:

  • Cookies对JavaScript来说默认是可用的(通过document.cookie),这使得一次XSS平滑升级能偷窃持久凭证(然后能使用Cookies在内存中产生类似于Spectre的有效攻击)。虽然十年前引入了HttpOnly属性,目前也只有大概8.31%的人使用Set-Cookie进行了设置。

  • 默认情况下,Cookie会被发送到非安全的源,这会导致凭据被盗。Secure属性将为安全的源Cookie锁定,这很好!尽管如此,今天只有大概7.85%的人Set-Cookie进行了设置。

  • Cookies经常在请求发送者没有任何迹象的情况下被设置。SameSite属性用于减少CSRF风险,但是事实上,目前只有大概0.06%的人使用Set-Cookie进行了设置。


Cookies使用属性来降低安全风险,然而它依然不符合我们对其他web资源设置的安全边界。它们通过给定的注册域名跨域访问,它们忽略端口和scheme(意味着它们很容易被网络攻击者伪造),它们能缩小到详细的路径。这些特征使得它们很难落地,很难为平台的其他地方降低同源策略起到激励作用。

效率低:服务能通过一个给定的注册域名存储很多Cookie,并且很多Cookie可以通过HTTP请求被发送。不同的浏览器供应商有不同的限制,但是它们都很高。例如Chrome,允许为每一个域名存储180个Cookie,相当于硬盘上的724kB。幸运的是,如果请求头中发送了很多的数据,服务器通常会出问题。Cookie滥用的情况事实上非常多:一般(未压缩)的Cookie请求头大小是409字节,但是90%的请求是1589字节,95%的请求是2549字节,99%的请求是4601字节(~0.1%的Cookie头非常大,超过10kB)。

隐私:Cookies一方面能开启监控(我希望我们可以在某种程度上解决这个问题,通过类似HTTP上Cookie的弊端描述的机制那样放弃它),另一方面,用户可能不了解全面跟踪的事情。

注意:上面提到的所有统计数据来自于Chrome的2018年7月的数据。我非常欢迎其他浏览器供应商提供类似的数据,但是我假定它们在同一个数量级。

提议


让我们通过为开发人员提供一条良好的路径来解决上述问题,可以起到防范安全的作用。用户代理能管理HTTP状态,相当于在用户这边通过为用户浏览的每个安全的源生成一个唯一的256位大小的Token。这个Token能作为结构化的HTTP请求头,交付给源:

Sec-HTTP-State: token=*J6BRKagRIECKdpbDLxtlNzmjKo8MXTjyMomIwMFMonM*

这个标识或多或少像代理控制的Cookie,包含一些重要的差别:

  1. 代理管理Token值,而非服务端管理。

  2. Token将仅仅在网络层可用,JavaScript不可用。

  3. 用户代理将只为每个源产生一个256位大小的Token,并且将不会暴露任何它产生的Token给源。

  4. Token将不会被产生,或者传递给不安全的源。

  5. 默认情况下,Token将会在相同网站的请求中传递。

  6. Token持续存在,直到它被服务或者用户或者代理重置。


这些差别可能不能覆盖所有的用户场景,但是一般情况下足够了。对于不够用的情况,我们支持开发者通过HTTP头的Sec-HTTP-State-Options选项来进行控制。选项如下:

  1. 一些服务将必须跨站访问它们的Token。另外一些服务可能希望将范围缩小到同源请求。两种方案都支持:

Sec-HTTP-State-Options: ..., delivery=cross-site, ...

或者

Sec-HTTP-State-Options: ..., delivery=same-origin, ...
  1. 一些服务将希望限制Token的生命周期。我们允许它们去设置TTL(单位秒):

Sec-HTTP-State-Options: ..., ttl=3600, ...

时间过期之后,这些Token的值将自动被重置。服务可能也希望去明确指定Token的重置(例如登出)。设置TTL为0能达到目的:

Sec-HTTP-State-Options: ..., ttl=0, ...

在任何情况下,都可以向当前运行的页面通知用户的状态变化,以便执行重置操作。当重置发生,用户代理能发送一个叫http-state-reset的消息到源的BroadcastChannel(并且可能唤醒服务器响应用户重置):

let resetChannel = new BroadcastChannel('http-state-reset')); resetChannel.onmessage = e => { /* Do exciting cleanup here. */ };
  1. 对一些服务来说,代理产生Token将足够状态维护。他们能像一个会话标识一样处理它,并且绑定用户状态到服务端。另一些服务需要额外工作来保证信任Token的来源。为此,服务能产生一个唯一id,服务端使用它跟会话id关联起来,并且通过HTTP响应头传递它到代理:

Sec-HTTP-State-Options: ..., key=*ZH0GxtBMWA...nJudhZ8dtz*, ...

代理将存储这个id,并且使用它去给数据集生成签名,减少Token抓取的风险:

Sec-HTTP-State: token=*J6BRKa...MonM*, sig=*(HMAC-SHA265(key, token+metadata))*

举例,使用已经定义的Signed Exchanges格式来签名请求,除了重放攻击之外,其他情况都很难使用被盗的令牌。在请求中包含时间戳可以减少重放攻击的可能性。

注意:这种特定情况没有被解决。我们需要去重新审视人们在绑定Token等方面所做的工作,以确定正确的威胁模型是怎么样的。把它当成是一个方向去探索,这还不是一个深思熟虑的稳妥解决方案。

FAQ


继续上面提到的三个方面,这个针对创建一个固化的状态Token的建议,映射到与平台其余部分的相同安全原语,减少客户端的传输成本,并且默认不支持跨站追踪。

我们应该放弃Cookie?

当然不是!

那为什么提出本方案?

Cookie是不好的,并且我们应该发现一种方式去放弃它。但是这需要花费一段时间。该提案旨在提供一种补充,即使在Cookie同时存在的情况下,也是有价值的方案,给我们一种推动开发者从一个方案增加另一个方案的能力。

与Cookie对比,它完全是一个新的东西?

不完全是。

开发人员可以通过设置像“__Host-token=value1; Secure; HttpOnly; SameSite=Lax; path=/”这样的Cookie来获得几乎所有上面提到的属性。这不是一个完美的方案,但它非常好。我了解到的现状是,开发人员需要了解各种标志和命名约定,才能通过Set-Cookie:token = value这样的方式使用它。默认值很重要,这似乎是最简单的事情。比起让开发人员使用4个属性,以及采用奇怪的命名约定来说,将它合并到1个属性里更好。

我们还可以重新考虑服务器端状态在代理上维护。我觉得使用用户代理控制会话标识符,而不是服务器设置的大量键值对,更优雅,更节约用户的资源。

你期待大家怎么从Cookie迁到这个方案上?

用户代理可以通过在传出的请求头附加Sec-HTTP-State的方式,逐步迁移,来广播对新功能的支持(默认情况下设置这个值,或允许开发人员根据上面的讨论选择性支持)。

开发人员可以开始将新机制用于其身份验证基础架构,这些机制最大程度地受益于源的作用域,与现有Cookie基础架构并行。随着时间的推移,他们可以建立一个他们所依赖的客户端状态列表,并开始在会话标识符和状态之间构建服务器端映射。一旦该机制成熟了,以逐步迁移的方式迁移。

最终,您可以想象让开发人员能够完全迁移,完全关闭其网站的Cookie(例如,通过Origin Manifests)。最终,我们还可以让开发人员选择使用Cookie而不是完全放弃。

在整个时间线的任何时刻,用户代理都可以像HTTP上Cookie的弊端里提到的那样,通过对Cookie子集进行限制来开始迁移。

对于多应用的源,这种迁移是不是很困难?

是的。这似乎是一个bug,又是一个功能。对于源和应用来说,1:1关系会更好。在同源的应用程序之间没有安全边界,我假设存在一个其他应用似乎没有多大价值。鼓励不同的应用在不同的源上运行,在它们的功能之间创建实际的隔离。

该提案是否对隐私属性有重大改变?

不完全是。绝大多数没有。也就是说,人们仍然可以使用这些Token来跟踪源的用户,就像他们今天使用Cookie一样。人们需要通过设置Token的delivery成员来声明这个意图,有一点小的要求,需要用户代理以某些方式对该声明作出合理的响应,但就其本身而言,技术能力几乎没有变化。

尽管如此,它仍然比现状有一些优势。例如:

  1. 这些Token不会以明文形式发送,这降低了监控的风险。

  2. 用户代理控制Token的值,这样可以降低在用户本地磁盘上暴露用户敏感信息的风险,降低暴露您和用户之间TLS终端的风险。

  3. 默认情况下,delivery选项会将Token限制为同源请求。假设我们遵循Cookie的SameSite 约定,用户跨站访问,需要明确说明Token可以跨站(此时用户代理才会做出相应的处理)。

用户可以如何管理用户代理?

就像今天使用Cookie一样,用户可以选择不将此Token发送到任何地方。用户代理会朝着那个目标努力,但用户也可以随时选择不适用用户代理。

相关阅读:


本文作者 Mike Wes,在Google的Chrome团队就职,邓启明翻译。转载本文请注明出处,欢迎更多小伙伴加入翻译及投稿文章的行列,详情请戳公众号菜单「联系我们」。


高可用架构

改变互联网的构建方式

长按二维码 关注「高可用架构」公众号



 
高可用架构 更多文章 Airbnb个性化搜索服务架构 Redis将继续采用BSD许可证 深度 | 从Go高性能日志库zap看如何实现高性能Go组件 Cloudflare Nginx优化成果:每天为互联网节约54年 重磅!GitHub发布开源负载均衡组件GLB
猜您喜欢 理解网络IO模型(二) Strong Ciphers——最强加密套件 【倒计时1天】2016微软开发者峰会 线上线下不见不散 不靠写缓存:更多百万IOPS的HA存储软件 深入理解 Flex 布局以及计算