SameSite的常见混淆
在这篇文章中,我剖析了关于SameSite Cookie属性的一个常见误解,并探讨了其对Web安全的潜在影响。
TL;DR ¶
- SameSite Cookie属性未被充分理解。
- 混淆“网站”和“源”是常见但有害的错误。
- “网站”的概念比看起来更复杂。
- 有些请求是跨源但同站的。
- SameSite仅对跨站请求有效。
- SameSite使你的子域名成为攻击目标。
- 误导性的实践可能导致过度回避SameSite=Strict。
SameSite的出现 ¶
你无疑听说过SameSite Cookie属性。它在2020年2月成为头条新闻,当时Chrome开始改变SameSite的默认行为。SameSite自2016年诞生以来,一直作为防御跨站攻击(如CSRF和XSSI)的深度防御机制,潜伏在浏览器的实现核心中。2020年初SameSite的激活需要一些网站进行费力调整以维持第三方访问,但被广泛誉为浏览器防御的受欢迎补充。
在SameSite激活前后,网络上的博客开始涌现文章,传播关于这一“新”Cookie属性机制的信息。
术语的随意使用 ¶
其中一些文章具有令人钦佩的精确性,例如Rowan Merewood在web.dev上发表的《SameSite cookies explained》。不幸的是,相对较少关于SameSite的文章努力澄清“网站”的概念,而“同站请求”和“跨站请求”的技术概念正是由此衍生。此外,许多文章,包括由信息安全社区有影响力的成员生产的文章,似乎将“源”和“网站”这两个术语互换使用,或至少使用得有些松散。
早在2019年2月,Kristian Bremberg在受人尊敬的Detectify博客上写道:
SameSite属性相当新,提供了出色的CSRF攻击防护。如果Cookie使用SameSite属性,Web浏览器将确保使用Cookie发出的请求来自设置Cookie的源。(我的强调)
信息安全超级明星Troy Hunt本人在2020年1月初发表的一篇开创性文章《Promiscuous Cookies and Their Impending Death via the SameSite Policy》中,描述了不同SameSite属性值的效果如下:
None:今天Chrome在没有设置SameSite值时的默认值
Lax:对跨源请求发送Cookie有一些限制
Strict:对跨源请求发送Cookie有严格限制
(我的强调)
几个月后,在一篇分析SameSite的出现如何影响黑客珍视的一系列漏洞的引人入胜的文章中,Reconless团队写道:
更新后,所有没有显式SameSite属性的Cookie将被视为具有SameSite=Lax。这意味着跨源请求不再携带Cookie,除了顶级导航。(我的强调)
域名、主机、源、网站…
在非正式交流中松散使用这些术语是很自然的;这种术语使用的松散性可以原谅,如果你自己也有过这样的问题,那你并不孤单。
“网站”和“源”可以互换吗? ¶
然而,SameSite Cookie属性的出现引发了一些问题……
- 这里是否需要仔细区分“源”和“网站”?
- 这是否只是一个没有区别的区别?
- 跨站请求与跨源请求没有不同吗?
- Cookie属性是否也可以命名为“SameOrigin”?
- 或者,如果“网站”和“源”之间确实存在真实差异,这对实践者重要吗?
- 如果差异重要,如何重要?
你可能已经从这篇文章的标题中猜到了答案:“网站”在SameSite的上下文中有一个非常技术性的含义,但它被不当忽视;网站和源之间的区别确实重要,但这两个概念经常被混淆。
术语上的这一失误并没有逃过所有人的注意。在Chrome激活SameSite仅几个月后,Google的Web开发者倡导者Eiji Kitamura觉得需要 dedicate一整篇博客文章来区分“源”和“网站”,这很能说明问题。
为了理解为什么这种区别重要,你首先需要理解源和网站之间的差异。
我们所说的“源”是什么意思? ¶
如果你使用Web技术,你至少对同源策略(SOP)有些熟悉,它可以说是Web安全的主要支柱之一。URI的源的概念当然是SOP的核心,因此,它相对较好理解。RFC 6454的第3.2节将源定义为一个三元组:
粗略地说,两个URI是同一源的一部分(即代表同一主体),如果它们具有相同的方案、主机和端口。端口是可选的;如果未指定,则暗示与方案关联的默认端口(例如,http为80,https为443)。
MDN Web Docs有一系列澄清示例。
我们所说的“网站”是什么意思? ¶
在“网站”这个极其通用的术语背后,隐藏着一个比源更难以掌握的概念。一方面,“网站”并不总是一个技术术语:它早于SOP,并且在跨站脚本等攻击出现时已被普遍使用。此外,现代网站的概念充满了技术困难。它与主机的可注册域密切相关,URL Living Standard将其定义为
[…] 由最具体的公共后缀以及紧接其前的域标签(如果有)组成的域。(主机的可注册域也称为其“eTLD+1”,即“有效顶级域加一”的缩写。)
在最简单的情况下,源的网站仅对应于源主机的可注册域(如果有)。两个例子,以固定思路:
- https://www.example.org的网站是example.org,因为org是主机的最具体公共后缀,因此example.org是主机的eTLD+1。
- https://jub0bs.github.io的网站是jub0bs.github.io,因为github.io是主机的最具体公共后缀,因此jub0bs.github.io是主机的eTLD+1。
是的!也许令人惊讶的是,github.io是一个公共后缀!然而,值得注意的是,可注册域的概念是流动的,因为它依赖于公共后缀列表(Public-Suffix List),这是一个不是一成不变而是会随时间变化的列表。更不用说不同的浏览器可能不一定以相同的速度跟上公共后缀列表的变化。
技术细节还不止于此!正如web.dev警告我们的那样,网站的概念仍在发展,并且很快将纳入方案。这一更改目前隐藏在Chrome的一个标志后面,但很快就会推出。然而,为了回避这一困难并延长本文的相关性,我在下文中仅考虑方案为https的源。
同站与跨站请求 ¶
既然我们已经处理了网站的概念,我们终于可以讨论同站和跨站请求的概念。一个给定的请求要么是同站的,要么是跨站的。请求是同站还是跨站取决于请求的源起源和目标起源的网站比较:
- 如果两个网站相同,则请求称为同站;
- 如果两个网站不同,则请求称为跨站。
这里有三个例子:
- 从https://foo.example.org发送到https://bar.example.org的请求是同站的,因为两个源的网站都是example.org。
- 从https://foo.github.io发送到https://bar.github.io的请求是跨站的,因为第一个源的网站是foo.github.io,而第二个源的网站是bar.github.io。
- 从https://foo.bar.example.org发送到https://bar.example.org的请求是同站的,因为两个源的网站都是example.org。
如果你已经读到这里,我感谢你的耐心。振作起来:回报近在眼前!
跨源、同站请求 ¶
所有跨站请求 necessarily 是跨源的;这一点很清楚。然而,正如第一个和第三个例子所示,以及下面粗糙的维恩图所说明的,并非所有跨源请求都是跨站的。
SameSite Cookie属性仅关注跨站请求;它对恰好是同站的跨源请求没有影响。这就是源和网站之间的区别重要的原因。
在线演示 ¶
为了证明我的观点,我从Troy Hunt的文章中汲取灵感,并部署了一个简单的Go服务器到samesitedemo.jub0bs.com,由两个端点组成。
端点 /setcookie
设置一个SameSite=Strict Cookie,如下所示:
|
|
端点 /readcookie
打印请求附带的Cookie(如果有)。
我还设置了两个“攻击”页面:
两个页面都只包含一个指向 https://samesitedemo.jub0bs.com/readcookie 的链接。
为了固定思路,以下是你可以做的:
- 导航到 https://samesitedemo.jub0bs.com/setcookie。这样做将在你的浏览器中设置Strict Cookie。
- 导航到 https://jub0bs.github.io/samesitedemo-attacker-foiled 并跟随该页面上的链接。由于攻击URI的网站(jub0bs.github.io)与目标URI的网站(jub0bs.com)不同,浏览器不会将Cookie附加到跟随链接产生的请求中,响应中不会打印任何Cookie。SameSite=Strict按预期工作,“攻击”被挫败。
- 现在导航到 https://samesitedemo-attacker.jub0bs.com/ 并跟随该页面上的链接。由于攻击URI的网站(jub0bs.com)与目标URI的网站(jub0bs.com)相同,浏览器确实将Cookie附加到跟随链接产生的请求中,并且Cookie确实在响应中打印。在这种情况下,SameSite Cookie属性根本不适用,“攻击”成功。
混淆网站和源的代价 ¶
虚假的安全感 ¶
暗示SameSite适用于所有跨源请求是有害的,因为它可能导致实践者错误地认为SameSite保护他们的用户免受所有跨源滥用。这种误解对于忽视审查其子域名安全级别的实践者尤其危险。特别是,
- 子域名接管,或
- 同一站点的子域名上的跨站脚本(XSS)实例,或
- 同一站点的子域名上的HTML注入实例
可能足以让攻击者绕过SameSite提供的相对保护。
子域名接管是一种早在2014年由Detectify推广的攻击。它利用子域名上的悬空DNS记录来控制该子域名服务的部分或全部内容。攻击者可以利用子域名接管达到各种目的:篡改、网络钓鱼等……以及否则不可能的跨源攻击!例如,如果攻击者能够接管 https://vulnerable.example.org,他或她可能能够从在易受攻击子域名上下文中运行的客户端代码发送恶意请求到 https://example.org(或其任何子域名);并且这些请求,由于是同站的,将携带所有相关的Cookie,无论它们的SameSite属性值如何!
对于这种跨源、同站攻击,甚至可能不需要子域名接管。同一站点的子域名上存在XSS或HTML注入漏洞可能就足以让攻击者发送恶意的跨源、同站请求。
注意:在上述两种情况下,“跨站请求伪造”是一个用词不当,因为攻击站点和目标站点是相同的;“同站跨源请求伪造”(SSCORF?)将是描述这种攻击的更合适的术语,但我怀疑它是否会达到普遍使用。
我发现 fascinating 的是,SameSite可能会将精明攻击者的注意力更集中在你的子域名和兄弟域名上,甚至比过去更多,因为这些域名正迅速成为针对经过战斗考验的Web应用程序的跨源攻击的唯一避难所。
采用SameSite=Strict较慢 ¶
此外,这种误解也可能减缓采用Strict值而支持Lax值。有些人确实 actively 劝阻实践者使用Strict,因为他们认为它对可用性的阻碍比实际更大。例如,在Dareboost的博客上,你可以读到以下声明:
如果我们在dareboost.com上使用Strict Same-Site,通过点击此链接,无论你是否连接,都不会被检测为已登录。
该声明不正确:所讨论的链接存在于 https://blog.dareboost.com 并指向 https://www.dareboost.com;因此,点击链接触发的请求将是同站的,并携带所有作用域在www子域名或父域名的Cookie。作者总结道:
这种行为可能会让最终用户感到困惑,因此你宁愿使用Lax模式。
这只是一个不公正贬低Strict值的例子,但我相信你可以在网络上的其他地方找到更多例子。
临别赠言 ¶
我已经联系了所有我认为在描述SameSite机制时不准确的人。到目前为止,只有Detectify的Kristian Bremberg和Reconless的Edwin Foudil(也称为@edoverflow)回复了我。我满怀希望他们会在阅读本文后修改他们的帖子。我在Troy Hunt的帖子上留下了公开评论,但他还没有回复我;我在Twitter上联系了Dareboost,但尚未收到他们的回复。
编辑(2021/02/10):Dareboost此后修改了他们的帖子。
记住:SameSite是保护用户免受跨站攻击的强大深度防御机制,但它对跨源、同站攻击无能为力。不要错过这个细微差别!否则,如果你在防御方,你可能会被一种虚假的安全感所麻痹,并且你可能被你认为不可能的 attacks blindsided;如果你在进攻方,你可能会错过漏洞发现,甚至可能错过可观的漏洞赏金。
附录(2021/01/31) ¶
自从本文首次发布以来,我发现了更多在描述SameSite机制时不谨慎使用“源”和“网站”术语的值得注意的实例。我没有尝试联系下面引用的文章的作者。
早在2016年,Qbit Cyber Security的Web应用程序黑客Sjoerd Langkemper在他的博客上写道:
此表显示了哪些Cookie随跨源请求发送。如你所见,没有same-site属性的Cookie […] 总是被发送。Strict Cookie从不发送。Lax Cookie仅随顶级get请求发送。(我的强调)
2018年5月,Artur Janc和Mike West(Same-site Cookies Internet Draft的作者本人)发布了一份报告(PDF),题为“How do we Stop Spilling the Beans Across Origins?”,其中你可以读到:
SameSite Cookie不直接防止攻击者加载跨源资源,但它们导致此类请求在没有凭据的情况下发送,使响应对攻击者价值不大。(我的强调)
最后,关于CSRF的维基百科页面本身声称:
如果此属性设置为“strict”,则Cookie仅在同源请求上发送,使CSRF无效。(我的强调)
编辑(2021/02/07):维基百科页面此后已被更正。
致谢 ¶
我要感谢Detectify的Fredrik N. Almroth,他 kindly 同意在发布前审阅本文的草稿。