SameSite Cookie介绍

通过显式声明跨域cookie以提高网站的安全性

Cookies是用于向网站添加持久状态的方法之一。多年来,它们的能力不断发展和壮大,但该平台遗留了一些问题。为了解决这个问题,浏览器(包括Chrome,Firefox和Edge)正在改变其行为,以强制实施更多的隐私保护默认值。

每个cookie都是一key=value对,并带有许多控制何时和何处使用cookie的属性。您可能已经使用这些属性来设置诸如到期日期之类的内容,或者指示cookie仅应通过HTTPS发送。服务器通过Set-Cookie在响应中发送适当命名的标头来设置Cookie 。

假设你有一个博客,你希望对你的用户展示一个更新内容的提示。用户可以点击取消,并且在一段时间内不再提示。你可以通过cookie来记录这个行为,将其设置为1个月过期(2,600,000秒),并且仅通过https传输这个cookie。则请求头如下:

Set-Cookie: promo_shown=1; Max-Age=2600000; Secure

当访问者满足如上条件,如通过https访问并且cookie没有超过过期时间(1个月),则浏览器会在请求时带上如下的请求头:

Cookie: promo_shown=1

何为一方Cookie和三方Cookie

您可能会注意到存在针对各种域的cookie,而不仅仅是您当前正在访问的cookie。与当前站点的域匹配的Cookie(即浏览器地址栏中显示的Cookie)称为第一方 Cookie。同样,来自当前站点以外的域的cookie也称为第三方 cookie。这不是绝对的,而是相对于用户的上下文。同一Cookie可以是第一方也可以是第三方,具体取决于用户当时所在的网站。

继续上面的示例,假设您的一篇博客文章中有一张特别令人惊奇的图片,该图片托管在 /blog/img/amazing-cat.png。因为这是一个热门图片,所以另一个人直接在他们的网站上使用它。如果访问者访问过您的博客并拥有 promo_shownCookie,则当他们浏览其他站点上引用你的 amazing-cat.png图片时,该图像请求中将发送 Cookie 。这是无用的,因为promo_shown对其他网站来说是不需要的cookie,只会增加请求开销。

什么时候会用到这个机制呢?通过这种机制,站点可以在第三方上下文中使用时保持状态。例如,如果您在自己的网站上嵌如腾讯视频,则访问者将在播放器中看到片头广告。如果您的访问者已经登录腾讯视频,并且是个VVIP会员,则该会话将通过第三方Cookie在嵌入式播放器中提供,也就是说,腾讯视频播放器将知道你是个交钱的用户和那些免费的穷B用户不一样,可以跳过广告或者查看VIP专属的广告。

网络文化之一是默认趋于开放状态,这也是为何很多人都愿意发布他们自己的内容和应用。但开放的同时也带来了一些隐私安全问题。跨站点请求伪造(CSRF)攻击依赖于将Cookie附加到给定源的任何请求,发起请求的是谁。例如,如果您访问evil.example那么它可以触发对的请求your-blog.example,并且您的浏览器会很高兴地附加关联的cookie(your-blog.example域下的cookie)。如果您的博客没有正确的处理这些请求,则evil.example可能触发删除帖子或添加自己的内容之类的操作。

用户也越来越意识到如何使用cookie来跟踪他们在多个站点上的活动。但是,到目前为止,还没有一种方法可以明确地表明您对Cookie的意图。您的promo_shownCookie仅应在第一方上下文中发送,而故意嵌入到其他站点的窗口小部件的会话cookie则有意在第三方上下文中提供登录状态。

使用SameSite属性显式声明Cookie的作用

sameSite属性允许你声明你的cookie仅限于一方使用或同一个站点的上下文使用。这里的站点指的是域后缀或域后缀的组合,如www.lyz810.com是lyz810.com站点的一部分。

如果用户在www.lyz810.com中请求一个来自img.lyz810.com的图片,则该请求为同一站点请求。

如果用户在blog.lyz810.com中请求一个来自img.lyz810.com的图片,则该请求为跨站点请求。

SameSite属性提供了3种方式控制cookie的行为。你可以选择不指定、指定严格模式Strict或宽松模式Lax来限制同站cookie策略。

如果设置,SameSite=Strict则意味着您的cookie仅在第一方上下文中发送。只有在Cookie的站点与浏览器的URL栏中当前显示的站点匹配时,才会发送cookie。因此,如果 promo_showncookie设置如下:

Set-Cookie: promo_shown=1; SameSite=Strict

当用户在您的网站上时,该cookie将与请求一起发送。但是,当通过链接打开您网站的时候,例如从另一个网站或通过朋友发送的电子邮件,在初始请求中,将不会发送cookie。如果您有与功能相关的Cookie,例如更改密码或购买链接,这些cookie只有在你打开链接后才会带上,而不会在初始页面就发送。

下面是通过SameSite=Lax来允许顶级导航发送cookie。让我们再次看下上面amazing-cat的网站示例,其中另一个网站引用了您的内容。他们直接利用您的猫照片,并提供指向您原始文章的链接。

<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

当读者从别人站点看你的猫时,不会发送cookie。但当访客通过链接打开你的博客时,那张猫图的请求会带着cookie。

注意:Lax和Strict不是网站安全的完整解决方案,cookie应该与其他用户输入同等看待,需要进行必要的验证,切不可储存服务端的机密数据。

如果不设置值,则隐式表示你希望所有上下文均发送cookie,最新草案中,引入新的值明确了这一点SameSite=None。可以使用None来显式声明在任何上下文中都发送cookie。

不设置SameSite时默认行为的改变

SameSite已经被广泛支持,但不幸的是开发者基本上都没有正确的使用它。默认开放所有上下文发送cookie功能不安全,易产生CSRF攻击和以外的信息泄露风险。为了鼓励开发人员显式声明cookie用途并为用户提供更安全的体验,IETF提案了2个改进:

  • 没有SameSite属性的Cookies 将被视为SameSite=Lax
  • SameSite=None 必须和 Secure 同时指定。

Chrome在80版本已经实现了该功能,Firefox正在测试并在后续版本中将设置为默认行为,Edge也考虑更改默认行为。