Cookie 的 SameSite 属性

作者: 阮一峰

日期: 2019年9月 9日

Chrome 51 开始,浏览器的 Cookie 新增加了一个SameSite属性,用来防止 CSRF 攻击和用户追踪。

一、CSRF 攻击是什么?

Cookie 往往用来存储用户的身份信息,恶意网站可以设法伪造带有正确 Cookie 的 HTTP 请求,这就是 CSRF 攻击。

举例来说,用户登陆了银行网站your-bank.com,银行服务器发来了一个 Cookie。


Set-Cookie:id=a3fWa;

用户后来又访问了恶意网站malicious.com,上面有一个表单。


<form action="your-bank.com/transfer" method="POST">
  ...
</form>

用户一旦被诱骗发送这个表单,银行网站就会收到带有正确 Cookie 的请求。为了防止这种攻击,表单一般都带有一个随机 token,告诉服务器这是真实请求。


<form action="your-bank.com/transfer" method="POST">
  <input type="hidden" name="token" value="dad3weg34">
  ...
</form>

这种第三方网站引导发出的 Cookie,就称为第三方 Cookie。它除了用于 CSRF 攻击,还可以用于用户追踪。

比如,Facebook 在第三方网站插入一张看不见的图片。


<img src="facebook.com" style="visibility:hidden;">

浏览器加载上面代码时,就会向 Facebook 发出带有 Cookie 的请求,从而 Facebook 就会知道你是谁,访问了什么网站。

二、SameSite 属性

Cookie 的SameSite属性用来限制第三方 Cookie,从而减少安全风险。

它可以设置三个值。

  • Strict
  • Lax
  • None

2.1 Strict

Strict最为严格,完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie。换言之,只有当前网页的 URL 与请求目标一致,才会带上 Cookie。


Set-Cookie: CookieName=CookieValue; SameSite=Strict;

这个规则过于严格,可能造成非常不好的用户体验。比如,当前网页有一个 GitHub 链接,用户点击跳转就不会带有 GitHub 的 Cookie,跳转过去总是未登陆状态。

2.2 Lax

Lax规则稍稍放宽,大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外。


Set-Cookie: CookieName=CookieValue; SameSite=Lax;

导航到目标网址的 GET 请求,只包括三种情况:链接,预加载请求,GET 表单。详见下表。

请求类型 示例 正常情况 Lax
链接 <a href="..."></a> 发送 Cookie 发送 Cookie
预加载 <link rel="prerender" href="..."/> 发送 Cookie 发送 Cookie
GET 表单 <form method="GET" action="..."> 发送 Cookie 发送 Cookie
POST 表单 <form method="POST" action="..."> 发送 Cookie 不发送
iframe <iframe src="..."></iframe> 发送 Cookie 不发送
AJAX $.get("...") 发送 Cookie 不发送
Image <img src="..."> 发送 Cookie 不发送

设置了StrictLax以后,基本就杜绝了 CSRF 攻击。当然,前提是用户浏览器支持 SameSite 属性。

2.3 None

Chrome 计划将Lax变为默认设置。这时,网站可以选择显式关闭SameSite属性,将其设为None。不过,前提是必须同时设置Secure属性(Cookie 只能通过 HTTPS 协议发送),否则无效。

下面的设置无效。


Set-Cookie: widget_session=abc123; SameSite=None

下面的设置有效。


Set-Cookie: widget_session=abc123; SameSite=None; Secure

三、参考链接

(完)

留言(52条)

有帮助

SameSite 增加了权限。问题的解决思路是 按情况规范限制。

希望Servlet4快点实现这个规范。csrf-token太麻烦了。

阮大神能讲讲ract+dva-cli+antd的用法吗,最近在开发中用到了,头很大,希望您能讲讲

那往后跨域请求想携带Cookie,只设置withCredentials已经不够了是吗?

引用鲁班七号的发言:

那往后跨域请求想携带Cookie,只设置withCredentials已经不够了是吗?

对,以后肯定是不行了,可以使用其他方式来做鉴权

引用橙子黄的发言:

对,以后肯定是不行了,可以使用其他方式来做鉴权

亲测跨子域(设置withCrendential)携带主域的cookie仍然可以哈,无论Lax/Strict模式。

有人知道谷歌计划什么时候设置Lax为默认值吗?
根据https://www.chromium.org/updates/same-site,貌似是Feb 17, 2020

现在好像还没开始执行啊

具体怎么用呢

完了完了,刚更新电脑,chrome80+已经强制执行了,线下环境测试无法登陆。后端说我的问题,我能怎么办?

引用小毛的发言:

亲测跨子域(设置withCrendential)携带主域的cookie仍然可以哈,无论Lax/Strict模式。

求阮一峰老师更正一下,我一开始看也是懵逼了好久,亲测,跨子域是没问题的,并非文章中说的“只有当前网页的 URL 与请求目标一致,才会带上 Cookie”

当samesite为lax的时候,script请求js会传cookie吗

引用理查德徐的发言:

有人知道谷歌计划什么时候设置Lax为默认值吗?
根据https://www.chromium.org/updates/same-site,貌似是Feb 17, 2020

你留言的时候已经更改了哈, Chrome 76(至少都是19年的事情了) 都已经修改默认值为Lax了, 官方说明: https://www.chromestatus.com/feature/5088147346030592

阮老师三言两语,就把一个技术难题讲的明明白白,受教了。

太坑了,最近遇到这个问题,发现响应头set-cookie不生效,是由于谷歌浏览器新增的SameSite,但是现在后端语言还没支持SameSite的API

引用louiebb的发言:

太坑了,最近遇到这个问题,发现响应头set-cookie不生效,是由于谷歌浏览器新增的SameSite,但是现在后端语言还没支持SameSite的API

PHP 7.3.0 已经支持了

本想iframe嵌套一个gitlab现在不行了。。。 只能让用户把gitlab和主网站在一个站点了 但是用户不愿意这么做 gitlab好像也没有地方去设置SameSite

最近的项目上遇到了这样的讨论,get了一波

第一个例子没看懂啊,银行请求的还是自己的服务器,有什么问题吗?

引用小毛的发言:

亲测跨子域(设置withCrendential)携带主域的cookie仍然可以哈,无论Lax/Strict模式。

需要注意 跨域请求 和 跨站请求的区别。 跨域请求不一定是跨站请求,而跨站请求一定是跨域请求。第三方 cookie 是针对跨站请求的。

引用KevinBlandy的发言:

希望Servlet4快点实现这个规范。csrf-token太麻烦了。

SameSite只能防范跨站的,如果在同站内伪造请求还是得使用token这样的验证措施。

为什么 form get 在 Lax 情况下会发送cookie呢?和ajax的 get 有什么区别

感觉就像是由后端验证 refer 属性,改为在前端验证,能部分抵御 RSRF,但倘若非法请求没有跨站,譬如嵌入某些论坛评论区,似乎就无效了。
感觉还是使用 token 会更安全些。

引用jelee的发言:

需要注意 跨域请求 和 跨站请求的区别。 跨域请求不一定是跨站请求,而跨站请求一定是跨域请求。第三方 cookie 是针对跨站请求的。

说得太好了

引用NoTryNoSuccess的发言:

SameSite只能防范跨站的,如果在同站内伪造请求还是得使用token这样的验证措施。

站内就没必要了。同站基本都是在自己人控制范围内的。

引用jelee的发言:

需要注意 跨域请求 和 跨站请求的区别。 跨域请求不一定是跨站请求,而跨站请求一定是跨域请求。第三方 cookie 是针对跨站请求的。

你确定你理解的是对的?跨域三要素,协议、域名、端口,这里面随便一个或者多个点不同,对于浏览器来说,还是同一站点????? 跨域 === 跨站 (当然,cookie是不区分端口的)

为什么我手动设置zhidu.com里的samesite为strict然后手动加入a标签跳转到百度,还是能带上cookie实现自动登录呢

form post 也会发送cookie (亲测),阮大神你试试

Latest update:

Mar 18, 2021: The flags #same-site-by-default-cookies and #cookies-without-same-site-must-be-secure have been removed from chrome://flags as of Chrome 91, as the behavior is now enabled by default. In Chrome 94, the command-line flag --disable-features=SameSiteByDefaultCookies,CookiesWithoutSameSiteMustBeSecure will be removed.

以后内网测试都不能测了...抛弃cookie吧

引用Ruby呀的发言:

你确定你理解的是对的?跨域三要素,协议、域名、端口,这里面随便一个或者多个点不同,对于浏览器来说,还是同一站点????? 跨域 === 跨站 (当然,cookie是不区分端口的)

“同站”只要两个 URL 的 eTLD+1 相同即可,不需要考虑协议和端口。其中,eTLD 表示有效顶级域名,注册于 Mozilla 维护的公共后缀列表(Public Suffix List)中,例如,.com、.co、.uk、.github.io 等。而 eTLD+1 则表示,有效顶级域名+二级域名,例如taobao.com等。举几个例子,www.taobao.com 和 www.baidu.com 是跨站,www.a.taobao.com 和 www.b.taobao.com 是同站,a.github.io 和 b.github.io 是跨站(注意是跨站)。

引用咚咚咚的发言:

Latest update:

Mar 18, 2021: The flags #same-site-by-default-cookies and #cookies-without-same-site-must-be-secure have been removed from chrome://flags as of Chrome 91, as the behavior is now enabled by default. In Chrome 94, the command-line flag --disable-features=SameSiteByDefaultCookies,CookiesWithoutSameSiteMustBeSecure will be removed.

以后内网测试都不能测了...抛弃cookie吧

请问您现在采用的是什么方法解决本地无法调试代码这个问题呢?我遇到了同样的问题

为什么 我webpack启动的前端 加上这个 请求都跨域了呢

引用小毛的发言:

亲测跨子域(设置withCrendential)携带主域的cookie仍然可以哈,无论Lax/Strict模式。

我试了是一下不行

阮老师,文中有一个地方不理解,就是Facebook 在第三方网站插入一张看不见的图片,如果第三方网站中加载了一张其它域名站点的图片,这个请求浏览器并不会将第三方网站的cookie发往facebook.com,这个是cookie的domain属性的发生的作用是已知的,所以文中说浏览器加载下面代码时,就会向 Facebook 发出带有 Cookie 的请求,&img src="facebook.com" style="visibility:hidden;">,是不是错了,还是这里有什么上下文我没带上导致理解错了?

引用AICC的发言:

阮老师,文中有一个地方不理解,就是Facebook 在第三方网站插入一张看不见的图片,如果第三方网站中加载了一张其它域名站点的图片,这个请求浏览器并不会将第三方网站的cookie发往facebook.com,这个是cookie的domain属性的发生的作用是已知的,所以文中说浏览器加载下面代码时,就会向 Facebook 发出带有 Cookie 的请求,&img src="facebook.com" style="visibility:hidden;">,是不是错了,还是这里有什么上下文我没带上导致理解错了?

Facebook的图片, 带的是Facebook的cookie.

在当前浏览器登录了Facebook的用户, 当其访问第三方网站时, "第三方网站引导发出的(Facebook的)Cookie,就称为第三方 Cookie".

document.cookie="widget_session=abc123; SameSite=None; Secure" 在哪里设置呢?

引用路人的发言:

Facebook的图片, 带的是Facebook的cookie.

在当前浏览器登录了Facebook的用户, 当其访问第三方网站时, "第三方网站引导发出的(Facebook的)Cookie,就称为第三方 Cookie".

不是,阮老师是讲没有samesite,请求会携带所有cookie

引用李扬翼的发言:

求阮一峰老师更正一下,我一开始看也是懵逼了好久,亲测,跨子域是没问题的,并非文章中说的“只有当前网页的 URL 与请求目标一致,才会带上 Cookie”


我也遇到了同样的ip一样,域名不一样,设置cookie为strict,还是会携带cookie

引用大大灰狼的发言:

感觉就像是由后端验证 refer 属性,改为在前端验证,能部分抵御 RSRF,但倘若非法请求没有跨站,譬如嵌入某些论坛评论区,似乎就无效了。
感觉还是使用 token 会更安全些。

那就是xss了,不是csrf,防住xss是前提

峰哥,最新的google浏览器版本,已经把samesite配置完全隐藏掉了,目前在前端和浏览器侧似乎已经没有办法绕过限制,传统的iframe框架网站,是不是只有反向代理这一条路来解决跨域请求场景需求了?请教一下。谢谢!

请问现在有什么办法可以解决呀

所以请求静态文件的缓存接口(http状态码为304的接口),request headers里面携带了Cookie字段,前端没办法把这个去掉吧?

所有怎么设置SameSite=None呢?从哪里设置呢?

strict

这个规则过于严格,可能造成非常不好的用户体验。比如,当前网页有一个 GitHub 链接,用户点击跳转就不会带有 GitHub 的 Cookie,跳转过去总是未登陆状态。

这句话什么意思,假如A页面有github链接,我点击链接跳转到github,那github的登陆态和A页面的cookie有什么关系?

引用宋的发言:

strict

这个规则过于严格,可能造成非常不好的用户体验。比如,当前网页有一个 GitHub 链接,用户点击跳转就不会带有 GitHub 的 Cookie,跳转过去总是未登陆状态。

这句话什么意思,假如A页面有github链接,我点击链接跳转到github,那github的登陆态和A页面的cookie有什么关系?

这个意思是当你同时登录A和B(github)两个站点,在A站点点击github链接进行跨站点访问时,如果设置strict,游览器不会把你github的cookie带上,这样你跳转github就是未登录态,此时只能主动刷新页面或者重新登录,实际和A站点的cookie没有关系。

iframe中嵌套的页面,通过src跳转页面,他怎样设置SameSite=None;

查了好久的资料,看到这里豁然开朗

https://github.com/newJcole/chrome-samesite-cookie
这个插件可以实现最新版本的跨站cookie共享

哈哈哈哈哈哈哈从MDN过来的,MDN的中文版讲得很懵,阮老师的这版好????????

lax时使用GET 表单也无法携带跨域 cookie了

文中提到的第三方Cookie【这种第三方网站引导发出的 Cookie,就称为第三方 Cookie。】和我理解的第三方Cookie是不一样的耶,我在网上查到的资料都是说,第三方Cookie是指:用户在访问网站A后,生成第一方Cookie,但在同一个浏览器中跨域访问网站B后,为网站B生成的Cookie叫做第三方Cookie。因为这些相对于网页A(第一方)和用户(第二方)的第三方,因此称为第三方Cookie。

我要发表看法

«-必填

«-必填,不公开

«-我信任你,不会填写广告链接