Cookie是便于向网站添加持久化状态的方式之一。随着时间推移,它们的能力得到了扩展和进化,也造成了很多历史遗留问题。为了解决这个问题,浏览器产商(包括Chrome,Firefox,和Edge)改变了他们的处理逻辑以加强个人隐私的默认配置。
每一个cookie都是拥有一些为了控制何时何处被使用的键值对。你可能已经通过这些属性去设置过期时间或者指明当前cookie只能在https协议下传输。服务器通过在响应中携带Set-Cookie
头来设置cookie。想要了解详细信息,可以深入阅读RFC6265bis,现在只是快速的回顾一下。
现在假设你的博客网站需要向用户推送一个“最新内容”的提示。用户可能会取消这个提示并且一段时间内他们也不会再看这个提示。你可以将这种喜好设置在一个cookie中,给它设置一个月(2,600,000 seconds)后到期,并且只能在HTTPS协议下传输。这个头部可能看起来像这样:
Set-Cookie: promo_shown=1; Max-Age=2600000; Secure
服务器通过Set-Cookie
头来设置cookie
当你的读者正在访问一个做了上面这些设置的页面,具体说来就是他们正在使用HTTPS进行连接并且这个cookie存续时间小于一个月,那么他们的浏览器就会请求里携带如下的头部:
Cookie: promo_shown=1
浏览器通过Cookie
头回传cookie
在javascript中通过使用document.cookie
可以读取或者添加站点的cookie。对document.cookie
进行赋值操作将会覆盖对应键的cookie。举个例子,尝试在你的浏览器javascript console中输入以下语句:
> document.cookie = "promo_shown=1; Max-Age=2600000; Secure"
< "promo_shown=1; Max-Age=2600000; Secure"
读取document.cookie
将会打印出所有能够在当前环境中获取到的cookie,每个cookie通过一个分号分隔
> document.cookie;
< "promo_shown=1; color_theme=peachpuff; sidebar_loc=left"
JavaScript中可以通过使用document.cookie
获取cookie
如果你尝试在一些流行的站点上读取cookie,你会明显发现它们中大多数设置了超过3个cookie。在大多数场景下,这些cookie将会根据一系列因素携带在对应域名的请求中。对于你的用户来说,一般上行带宽一般比下行带宽拥有更多的限制,所以将大量cookie携带在对外请求上将会导致获取第一个字节的延迟。为了合理控制设置的cookie数量。通过设置Max-Age
属性来帮助保证cookie在不需要的时候及时被清除掉。
什么是自有和第三方cookie?
重新访问前面一系列站点,很可能会发现有些cookie会在很多不同域名下出现,不仅仅是当前正在访问站点域名。当前站点域名(显示在浏览器地址栏里面的域名),被称为自有域名。同样的,那些来自当前站点外域名的cookie被称为第三方域名。这只是相对用户的角度并不是一个绝对的标签,同样的cookie既可能是自有的也可能是第三方的,取决于用户当前访问的站点。
一个页面上的cookie可能来自多个不同域名
继续前面的例子,假设你的一篇博客当中有一张极其可爱的小猫图片,这张图片存储在/blog/img/amazing-cat.png
下。由于这张图片太可爱了,其他人在他们的网站上直接使用了它。如果一个用户访问过你的博客将会有promo_shown cookie,当他在其他网站上看到这张直接被引用的图片时,图片的请求将会携带这个cookie。这并不是对任何人都特别有用,因为promo_shown在其他人的网站上并不是对任何请求都有用,这样只会增加请求的负载。
如果这并不是有意达成的效果,那在什么情况下你需要这样了。这是一种允许在第三方站点环境上保持会话状态的机制。例如,如果你的站点页面上添加了一个YouTube播放器,那么访问者将会在播放器中看到一个"watch later"的操作项。如果访问者已经登陆了YouTube,第三方cookie将会使得用户通过一步点击"watch later"按钮收藏这个视频到YouTube供后续观看,而不需要提醒他们登陆YouTube或者离开当前站点并跳转到YouTube。
在第三方环境下访问不同的页面cookie将被传输
万维网文化属性之一是开放共识。这使得许多人创造自己的内容和应用程序成为可能。然而这也带来了一些安全和隐私风险。跨站请求伪造攻击(Cross-site request forgery attack-csrf)制造依赖于那些cookie将会被携带在针对指定域名的请求中,而不论这些请求具体由谁触发。举个例子,当你访问了evil.example
并且它可以触发your-blog.example
的请求,那么浏览器将会很乐意携带上关联的cookie。如果你的博客对这些请求的验证不够仔细那么evil.example
可以触发删除和添加内容的任何操作。
用户现在更加清醒的意识到cookie怎样被用来跟踪他们在不同页面的活动轨迹。然而直到现在仍然没有一种明确的方式表明对cookie本来用意。你的promo_shown
cookie本应该只被用在自有站点环境下,而嵌入到第三方站点的组件所携带的会话cookie是被用来保持会话状态。
使用SameSite明确表明cookie的用途
SameSite属性的引入(定义在RFC6265bis)允许你表明你的cookie被用在第三方或者自己的站点环境下。搞清楚站点的确切意义将会大有裨益。站点是域名后缀和它前面部分的混合。例如,www.web.dev
域名站点是web.dev
域名站点的一部分。
关键词same-site
如果用户在www.web.dev
发起了一个向static.web.dev
的图片请求这是一个same-site
请求。
public suffix list定义了这些,不仅是像.com
这样的顶级域名也包括像github.io
这样的域名。这使得your-project.github
和my-project.github
被认为为不同的站点。
关键词cross-site
如果用户在your-project.github.io
站点发起了一个向my-project.github.io
的请求,这是一个cross-site
请求。
在cookie中引入SameSite
属性三种控制这种行为的方式。你可以不指定指定这个属性,或者通过使用Strict
或Lax
来限制cookie只在same-site
请求下携带。
如果你设置SameSite
属性为Strict
,那么你的cookie将会只在自有环境下发送。在用户看来,那个cookie只会在匹配当前显示在地址栏的站点时才会被发送。所以,假设Promo_shown
cookie如下设置:
Set-Cookie: promo_shown=1; SameSite=Strict
那么当你的用户在你的站点上时,cookie将会预期发送。然而当从一个指向你站点的引用链接跳转时,详细地说当从另一个站点或者通过一个朋友发给你的一个链接跳转时,在初始请求里这个cookie并不会被发送。当你拥有一些关联后置于初始导航你站点功能cookie的情形下,例如修改密码或者下订单的情形,这是极其有用的,但是对于promo_shown
这个功能限制就太多了。如果你的用户通过链接进入你的站点,他们希望cookie能被发送,这样他们的设置能够生效。
当你的用户初始导航到你的网站上时cookie将会被发送,这就是SameSite=Lax
的使用场景。让我们继续回顾那个引用小猫图片的例子,那个例子中其他站点正在引用你的内容。他们直接使用你的小猫图片并且提供了一个指向你的原始文章的链接地址。
<p>Look at this amazing cat!</p>
<img src="https://blog.example/blog/img/amazing-cat.png" />
<p>Read the <a href="https://blog.example/blog/cat.html">article</a>.</p>
cookie这样设置:
Set-Cookie: promo_shown=1; SameSite=Lax
当浏览者在其他人的博客上请求amzing-cat.png
,cookie将不会被发送。然而当读者通过点击一个指向你站点cat.html
链接时,cookie将会被发送。这样的话,使得Lax
成为设置影响网站外观cookie的好选择而Strict
将会对那些关联到用户当前操作的cookie极其有用。
注意
Strict
和Lax
对于你的站点安全都不是一个完整的解决方案。cookie将会作为用户输入的一部分,你需要像对待其他输入一样。意味着,需要对这些输入进行脱敏和验证。绝对不要将服务端的重要数据存储在cookie中。
最后还有一个不设置这个值的默认行为,这种情形下cookie将会在所有上下文情形下被发送。在最新的RFC6265bis草案中这种方式被SameSite=None
这个新值明确表达。这表明你可以通过设置SameSite=None
来明确表明你希望cookie在所有第三方上下文环境下被发送。
!(None Lax Strict)[https://webdev.imgix.net/samesite-cookies-explained/samesite-none-lax-strict.png]
通过None
,Lax
和Strict
来明确表明cookie的使用上下文
★ 如果你提供了供其他网站使用的服务,例如嵌入式组件、嵌入式多媒体内容、联合程序、广告、跨站登陆那么你需要使用None
来明确表明的意图。
未设置SameSite属性默认行为的变化
尽管SameSite
属性得到广泛支持,很不幸的是并没有很广泛的被开发者使用。cookie在所有地方都会被发送的开放默认行为使得所有场景都能正常工作,但是这样提高了用户受到CSRF和意外信息泄漏的风险指数。为了号召开发者明确表明他们的意图并且提供用户更高的安全使用体验,IETF发布了一个提议,Incrementally Better Cookies 提供了两项关键改进:
- 那些没有设置
SameSite
属性的cookie将等同设置了SameSite=Lax
- cookie设置
SameSite=None
必须同时设置Secure
,这意味着需要在更安全的环境下传输。
Chrome在84版本实现了以上默认行为。Firefox在69版本可以测试使用,并且未来会加入默认配置中。为了在Firefox中测试这些行为,打开about:config
然后network.cookie.sameSite.laxByDefault
。Edge也计划改变其默认行为。