A cookie associated with a cross-site resource at <URL> was set without the `SameSite` attribute. It has been blocked, as Chrome now only delivers cookies with cross-site requests if they are set with `SameSite=None` and `Secure`. You can review cookies in developer tools under Application>Storage>Cookies and see more details at <URL> and <URL>.
Cookies是可用于向网站添加持久状态的方法之一。多年来,他们的能力不断发展和壮大,但遗留了一些有问题的遗留问题。为了解决这个问题,浏览器(包括Chrome,Firefox和Edge)正在更改其行为,以强制实施更多保留隐私的默认设置。
每个cookie是一个key=value
对以及一些控制何时和何处使用该cookie的属性。您可能已经使用这些属性来设置诸如到期日期之类的信息,或者指示cookie仅应通过HTTPS发送。服务器通过在响应中发送适当命名的Set-Cookie
标头来设置cookie。有关所有详细信息,您可以深入研究RFC6265bis,但现在这里是快速更新。
假设您有一个博客,希望在其中向用户显示“新功能”促销。用户可以撤消该促销,然后过一会儿再看不到它。您可以将该首选项存储在cookie中,将其设置为在一个月(2,600,000秒)内到期,然后仅通过HTTPS发送。该标题看起来像这样:
Set-Cookie: promo_shown=1; Max-Age=2600000; Secure

服务器使用Set-Cookie标头设置cookie
当您的读者查看满足这些要求的页面时,即他们处于安全连接并且Cookie的使用期限不到一个月,那么他们的浏览器就会在其请求中发送此标头:
Cookie: promo_shown=1

您的浏览器将Cookie发送回Cookie标头中
您还可以使用document.cookie在JavaScript中添加和读取可用于该站点的cookie。 对document.cookie进行分配将使用key创建或覆盖cookie。 例如,您可以在浏览器的JavaScript控制台中尝试以下操作:
> 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。 在大多数情况下,这些cookie是在每个单个请求上发送到该域的,这有很多含义。对于您的用户,上传带宽通常比下载带宽受到更多限制,因此所有出站请求的开销都会增加您的第一个字节的时间延迟。 保守您设置的Cookie的数量和大小。利用Max-Age
属性可帮助确保cookie的停留时间不会超过所需的时间。
什么是第一方和第三方Cookie?
您可能会注意到存在针对各种域的cookie,而不仅仅是您当前正在访问的cookie。 与当前网站的域匹配的Cookie(即浏览器地址栏中显示的Cookie)称为第一方Cookie。 同样,来自当前站点以外的域的cookie也称为第三方cookie。 这不是绝对的标签,而是相对于用户的上下文。 同一Cookie可以是第一方也可以是第三方,这取决于用户当时所在的网站。

Cookie可能来自一页上的各种不同域
假设您的一篇博客文章中包含一张特别令人惊叹的猫的图片,并将其托管在/blog/img/amazing-cat.png
。因为这是一个了不起的图像,所以另一个人直接在他们的网站上使用它。如果访问者访问过您的博客并具有promo_shown cookie
,那么当他们在其他人的站点上查看amazing-cat.png
时,将在该图像请求中发送cookie。这对任何人都不是特别有用,因为promo_shown
并未用于此人网站上的任何内容,它只是增加了请求的开销。
如果那是意想不到的效果,那么为什么要这样做呢?通过这种机制,站点可以在第三方上下文中使用时保持状态。例如,如果您在自己的网站上嵌入YouTube视频,则访问者将在播放器中看到“稍后观看”选项。如果您的访问者已经登录YouTube,则该会话将通过第三方Cookie在嵌入式播放器中提供-意味着“稍后观看”按钮将立即保存视频,而不是提示他们登录或必须将它们从您的页面上移开,然后返回到YouTube。

当访问不同页面时,将发送第三方上下文中的cookie
网络的文化属性之一是默认情况下它倾向于打开。这是让如此多的人在那里创建自己的内容和应用程序的一部分。但是,这也带来了许多安全和隐私问题。跨站点请求伪造(CSRF)攻击依赖于将Cookie附加到给定源的任何请求这一事实,无论谁发起该请求。例如,如果您访问evil.example
,则它可以触发对your-blog.example
的请求,并且您的浏览器会很高兴地附加关联的cookie。如果您的博客对如何验证这些请求不谨慎,那么evil.example
可能会触发删除帖子或添加自己的内容之类的操作。
用户也越来越意识到如何使用cookie来跟踪他们跨多个站点的活动。但是,到目前为止,还没有一种方法可以明确地表示您对Cookie的意图。您的promo_shown
Cookie仅应在第一方上下文中发送,而故意嵌入到其他站点的窗口小部件的会话cookie则有意在第三方上下文中提供登录状态。

将Cookie的上下文明确标记为None,Lax或Strict
如果您提供其他站点使用的服务,例如小部件,嵌入式内容,会员计划,广告或跨多个站点登录,则应使用
None
以确保您的意图明确。
使用SameSite
属性明确声明Cookie的使用情况
SameSite
属性(在RFC6265bis中定义)的引入使您可以声明cookie是否应限于第一方或同一站点上下文。 准确了解“站点”在这里的含义是有帮助的。 该站点是域后缀和域后缀的组合。 例如,www.web.dev
域是web.dev
站点的一部分。
如果用户位于
www.web.dev
上,并从static.web.dev
请求图像,则该图像是同一站点的请求。
公共后缀列表对此进行了定义,因此它不仅是.com之类的顶级域名,还包括github.io之类的服务。 这使your-project.github.io和my-project.github.io可以算作单独的站点。
如果用户在
your-project.github.io
上,并从my-project.github.io
请求图像,则这是跨站点请求。
在Cookie上引入SameSite
属性可提供三种不同的方式来控制此行为。 您可以选择不指定属性,也可以使用Strict
或Lax
将cookie限制为同一站点请求。
如果将SameSite
设置为Strict
,则cookie仅在第一方上下文中发送。 用用户术语讲,只有在Cookie的站点与浏览器的URL栏中当前显示的站点匹配时,才会发送cookie。 因此,如果将promo_shown
cookie设置如下:
Set-Cookie: promo_shown=1; SameSite=Strict
当用户在您的网站上时,该Cookie将与预期的请求一起发送。 但是,当跟随一个链接到您网站的链接时,例如从另一个网站或通过朋友发送的电子邮件,在该初始请求中,将不会发送cookie。 当您有与功能相关的cookie总是处于初始导航之后(例如更改密码或购买商品),但对promo_shown
的限制过于严格时,这很好。 如果您的读者访问该站点的链接,则他们希望发送Cookie,以便可以应用其首选项。
SameSite=Lax
的出现,它允许通过这些顶级导航发送cookie。 让我们从上面重新访问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
当读者在其他人的博客上时,如果浏览器请求amazing-cat.png
,则不会发送该cookie。 但是,当读者通过链接访问您博客上的cat.html
时,该请求将包含cookie。 这使得Lax
成为影响网站显示的Cookie的不错选择,而Strict
对于与您的用户执行的操作相关的Cookie很有用。
Strict和Lax都不是您网站安全的完整解决方案。Cookie是作为用户请求的一部分发送的,您应该将它们与其他任何用户输入一样对待。 这意味着对输入进行消毒和验证。 切勿使用cookie来存储您认为是服务器端机密的数据。
最后,可以选择不指定值,该值以前是隐式声明您希望在所有上下文中发送cookie的方式。 在RFC6265bis的最新草案中,通过引入SameSite=None
的新值来明确指出这一点。 这意味着您可以使用“无”来明确表示您有意要在第三方上下文中发送cookie。
在没有SameSite的情况下更改默认行为
尽管SameSite
属性得到了广泛支持,但不幸的是,它尚未被开发人员广泛采用。 开放的默认发送Cookie到处意味着所有用例都可以使用,但使用户容易受到CSRF和意外信息泄漏的影响。 为了鼓励开发人员陈述其意图并为用户提供更安全的体验,IETF提案《Incrementally Better Cookies》提出了两个关键更改:
- 没有
SameSite
属性的Cookie将被视为SameSite=Lax
。 SameSite=None
的Cookie也必须指定Secure
,这意味着它们需要安全的上下文。
Chrome自80版本起就实现了这些行为。Firefox从Firefox 69起就可以对其进行测试,并将在将来使它们成为默认行为。 要在Firefox中测试这些行为,请打开about:config
并设置network.cookie.sameSite.laxByDefault
。 Edge也在计划更改其默认行为。
注意
默认SameSite=Lax
未设置属性
Set-Cookie: promo_shown=1
如果发送未指定任何SameSite
属性的Cookie,则应用默认行为
Set-Cookie: promo_shown=1; SameSite=Lax
浏览器将将该cookie视为已指定SameSite=Lax
。
虽然这是为了应用更安全的默认值,但理想情况下,您应该设置一个明确的SameSite
属性,而不要依赖浏览器为您应用该属性。 这使您对Cookie的意图明确,并提高了跨浏览器获得一致体验的机会。
与默认的SameSite=Lax
相比,Chrome所应用的默认行为要宽松一些,因为它将允许某些Cookie在顶级POST请求上发送。 您可以在the blink-dev announcement中看到确切的详细信息。 这只是暂时的缓解措施,您仍应修复跨站点Cookie以使用SameSite=None; Secure
。
SameSite=None必须加上secure
Set-Cookie: widget_session=abc123; SameSite=None
设置没有Secure的cookie将被拒绝。
Set-Cookie: widget_session=abc123; SameSite=None; Secure
您必须确保将SameSite=None与Secure属性同时使用。
您可以通过启用chrome://flags/#cookies-without-same-site-must-be-secure
来测试从Chrome 76开始的此行为和通过about.config
从Firefox 69中测试该行为,方法是设置network.cookie.sameSite.noneRequiresSecure
。
您将需要在设置新的Cookie时应用此功能,并主动刷新现有的Cookie,即使这些Cookie的有效期限未到。
如果您依赖于在您的站点上提供第三方内容的任何服务,则还应该向提供商咨询他们正在更新其服务。 您可能需要更新依赖项或代码片段,以确保您的网站采用了新的行为。
这两个更改都与已正确实现SameSite
属性的先前版本或根本不支持它的浏览器向后兼容。 通过将这些更改应用于Cookie,您可以明确指定其预期用途,而不是依赖浏览器的默认行为。 同样,任何尚未识别SameSite=None
的客户端都应忽略它,并像未设置属性一样继续操作。
许多旧版本的浏览器(包括Chrome,Safari和UC浏览器)与新的
None
属性不兼容,并且可能会忽略或限制Cookie。 此行为在当前版本中已修复,但是您应该检查流量以确定受影响的用户比例。 您可以在查询list of known incompatible clients on the Chromium site。
有关准确更新Cookie的详细信息,以成功处理对SameSite=None的这些更改以及浏览器行为的差异,请转到后续文章SameSite cookie 指南