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 | 不发送 |
设置了Strict
或Lax
以后,基本就杜绝了 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
三、参考链接
- Using the Same-Site Cookie Attribute to Prevent CSRF Attacks
- SameSite cookies explained
- Tough Cookies, Scott Helme
- Cross-Site Request Forgery is dead!, Scott Helme
(完)
情非得已 说:
有帮助
2019年9月 9日 18:01 | # | 引用
连明堂 说:
SameSite 增加了权限。问题的解决思路是 按情况规范限制。
2019年9月 9日 18:24 | # | 引用
KevinBlandy 说:
希望Servlet4快点实现这个规范。csrf-token太麻烦了。
2019年9月 9日 22:08 | # | 引用
谷雨 说:
阮大神能讲讲ract+dva-cli+antd的用法吗,最近在开发中用到了,头很大,希望您能讲讲
2019年9月12日 08:49 | # | 引用
鲁班七号 说:
那往后跨域请求想携带Cookie,只设置withCredentials已经不够了是吗?
2019年9月12日 09:03 | # | 引用
橙子黄 说:
对,以后肯定是不行了,可以使用其他方式来做鉴权
2020年1月14日 13:54 | # | 引用
小毛 说:
亲测跨子域(设置withCrendential)携带主域的cookie仍然可以哈,无论Lax/Strict模式。
2020年1月16日 20:59 | # | 引用
理查德徐 说:
有人知道谷歌计划什么时候设置Lax为默认值吗?
根据https://www.chromium.org/updates/same-site,貌似是Feb 17, 2020
2020年2月11日 14:21 | # | 引用
大叔 说:
现在好像还没开始执行啊
2020年3月11日 15:09 | # | 引用
辉 说:
具体怎么用呢
2020年3月12日 14:35 | # | 引用
玄晓乌屋 说:
完了完了,刚更新电脑,chrome80+已经强制执行了,线下环境测试无法登陆。后端说我的问题,我能怎么办?
2020年4月 2日 15:24 | # | 引用
李扬翼 说:
求阮一峰老师更正一下,我一开始看也是懵逼了好久,亲测,跨子域是没问题的,并非文章中说的“只有当前网页的 URL 与请求目标一致,才会带上 Cookie”
2020年4月 2日 16:39 | # | 引用
风缘 说:
当samesite为lax的时候,script请求js会传cookie吗
2020年4月 3日 10:00 | # | 引用
自娱自乐而已 说:
你留言的时候已经更改了哈, Chrome 76(至少都是19年的事情了) 都已经修改默认值为Lax了, 官方说明: https://www.chromestatus.com/feature/5088147346030592
2020年4月18日 00:23 | # | 引用
客青 说:
阮老师三言两语,就把一个技术难题讲的明明白白,受教了。
2020年4月24日 14:36 | # | 引用
louiebb 说:
太坑了,最近遇到这个问题,发现响应头set-cookie不生效,是由于谷歌浏览器新增的SameSite,但是现在后端语言还没支持SameSite的API
2020年8月24日 14:18 | # | 引用
qiqishibing 说:
PHP 7.3.0 已经支持了
2020年8月24日 17:48 | # | 引用
Orange 说:
本想iframe嵌套一个gitlab现在不行了。。。 只能让用户把gitlab和主网站在一个站点了 但是用户不愿意这么做 gitlab好像也没有地方去设置SameSite
2020年9月 8日 17:34 | # | 引用
jonny-xhl 说:
最近的项目上遇到了这样的讨论,get了一波
2020年9月23日 11:57 | # | 引用
胜利之日 说:
第一个例子没看懂啊,银行请求的还是自己的服务器,有什么问题吗?
2020年10月 5日 11:59 | # | 引用
jelee 说:
需要注意 跨域请求 和 跨站请求的区别。 跨域请求不一定是跨站请求,而跨站请求一定是跨域请求。第三方 cookie 是针对跨站请求的。
2020年10月13日 15:29 | # | 引用
NoTryNoSuccess 说:
SameSite只能防范跨站的,如果在同站内伪造请求还是得使用token这样的验证措施。
2020年11月 1日 10:39 | # | 引用
月出 说:
为什么 form get 在 Lax 情况下会发送cookie呢?和ajax的 get 有什么区别
2020年11月25日 01:43 | # | 引用
大大灰狼 说:
感觉就像是由后端验证 refer 属性,改为在前端验证,能部分抵御 RSRF,但倘若非法请求没有跨站,譬如嵌入某些论坛评论区,似乎就无效了。
感觉还是使用 token 会更安全些。
2020年12月27日 22:50 | # | 引用
foxpsd 说:
说得太好了
2021年1月12日 20:04 | # | 引用
foxpsd 说:
站内就没必要了。同站基本都是在自己人控制范围内的。
2021年1月12日 20:05 | # | 引用
Ruby呀 说:
你确定你理解的是对的?跨域三要素,协议、域名、端口,这里面随便一个或者多个点不同,对于浏览器来说,还是同一站点????? 跨域 === 跨站 (当然,cookie是不区分端口的)
2021年1月22日 10:29 | # | 引用
hank 说:
为什么我手动设置zhidu.com里的samesite为strict然后手动加入a标签跳转到百度,还是能带上cookie实现自动登录呢
2021年2月 3日 11:55 | # | 引用
青蛙表哥 说:
form post 也会发送cookie (亲测),阮大神你试试
2021年2月19日 14:58 | # | 引用
咚咚咚 说:
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吧
2021年6月 2日 10:34 | # | 引用
harris 说:
“同站”只要两个 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 是跨站(注意是跨站)。
2021年6月 8日 17:13 | # | 引用
Lee 说:
请问您现在采用的是什么方法解决本地无法调试代码这个问题呢?我遇到了同样的问题
2021年6月10日 10:28 | # | 引用
kill 说:
为什么 我webpack启动的前端 加上这个 请求都跨域了呢
2021年6月25日 11:41 | # | 引用
Sunny 说:
我试了是一下不行
2021年7月20日 20:37 | # | 引用
AICC 说:
阮老师,文中有一个地方不理解,就是Facebook 在第三方网站插入一张看不见的图片,如果第三方网站中加载了一张其它域名站点的图片,这个请求浏览器并不会将第三方网站的cookie发往facebook.com,这个是cookie的domain属性的发生的作用是已知的,所以文中说浏览器加载下面代码时,就会向 Facebook 发出带有 Cookie 的请求,&img src="facebook.com" style="visibility:hidden;">,是不是错了,还是这里有什么上下文我没带上导致理解错了?
2021年8月10日 12:34 | # | 引用
路人 说:
Facebook的图片, 带的是Facebook的cookie.
在当前浏览器登录了Facebook的用户, 当其访问第三方网站时, "第三方网站引导发出的(Facebook的)Cookie,就称为第三方 Cookie".
2021年8月12日 10:51 | # | 引用
大黄 说:
document.cookie="widget_session=abc123; SameSite=None; Secure" 在哪里设置呢?
2021年8月13日 17:58 | # | 引用
路人2 说:
不是,阮老师是讲没有samesite,请求会携带所有cookie
2021年8月31日 19:30 | # | 引用
彭晓琪 说:
我也遇到了同样的ip一样,域名不一样,设置cookie为strict,还是会携带cookie
2022年1月 6日 20:49 | # | 引用
shijinupc 说:
那就是xss了,不是csrf,防住xss是前提
2022年1月17日 11:27 | # | 引用
归来少年 说:
峰哥,最新的google浏览器版本,已经把samesite配置完全隐藏掉了,目前在前端和浏览器侧似乎已经没有办法绕过限制,传统的iframe框架网站,是不是只有反向代理这一条路来解决跨域请求场景需求了?请教一下。谢谢!
2022年2月24日 11:19 | # | 引用
感谢 说:
请问现在有什么办法可以解决呀
2022年3月18日 19:09 | # | 引用
yya 说:
所以请求静态文件的缓存接口(http状态码为304的接口),request headers里面携带了Cookie字段,前端没办法把这个去掉吧?
2022年6月14日 08:49 | # | 引用
中中 说:
所有怎么设置SameSite=None呢?从哪里设置呢?
2022年6月20日 18:37 | # | 引用
宋 说:
strict
这个规则过于严格,可能造成非常不好的用户体验。比如,当前网页有一个 GitHub 链接,用户点击跳转就不会带有 GitHub 的 Cookie,跳转过去总是未登陆状态。
这句话什么意思,假如A页面有github链接,我点击链接跳转到github,那github的登陆态和A页面的cookie有什么关系?
2022年7月 4日 11:32 | # | 引用
absoluty 说:
这个意思是当你同时登录A和B(github)两个站点,在A站点点击github链接进行跨站点访问时,如果设置strict,游览器不会把你github的cookie带上,这样你跳转github就是未登录态,此时只能主动刷新页面或者重新登录,实际和A站点的cookie没有关系。
2022年8月 2日 13:47 | # | 引用
曹明明 说:
iframe中嵌套的页面,通过src跳转页面,他怎样设置SameSite=None;
2022年9月 9日 17:42 | # | 引用
qihai 说:
查了好久的资料,看到这里豁然开朗
2022年11月26日 15:44 | # | 引用
peace 说:
https://github.com/newJcole/chrome-samesite-cookie
这个插件可以实现最新版本的跨站cookie共享
2022年12月16日 16:28 | # | 引用
不知名工程师 说:
哈哈哈哈哈哈哈从MDN过来的,MDN的中文版讲得很懵,阮老师的这版好????????
2022年12月18日 17:15 | # | 引用
panda 说:
lax时使用GET 表单也无法携带跨域 cookie了
2022年12月20日 20:07 | # | 引用
xuyaqist 说:
文中提到的第三方Cookie【这种第三方网站引导发出的 Cookie,就称为第三方 Cookie。】和我理解的第三方Cookie是不一样的耶,我在网上查到的资料都是说,第三方Cookie是指:用户在访问网站A后,生成第一方Cookie,但在同一个浏览器中跨域访问网站B后,为网站B生成的Cookie叫做第三方Cookie。因为这些相对于网页A(第一方)和用户(第二方)的第三方,因此称为第三方Cookie。
2023年9月18日 16:03 | # | 引用