Cookie 的 SameSite 属性

今天在做前后端分离项目的时候遇到了这样一个问题。
设置了与跨站点资源http://www.****.com/关联的cookie,但没有设置' SameSite '属性。在未来的Chrome版本中,只有当跨站请求设置为“SameSite=None”和“Secure”时,才会发送cookie。您可以在应用程序>存储> cookies下查看开发工具中的cookie,并在https://www.chromestatus.com/feature/5088147346030592和https://www.chromestatus.com/feature/5633521622188032查看更多详细信息。

去查了资料后发现,因为 HTTP 协议是无状态的,所以很久以前的网站是没有登录这个概念的,直到网景发明 cookie 以后,网站才开始利用 cookie 记录用户的登录状态。cookie 是个好东西,但它很不安全,其中一个原因是因为 cookie 最初被设计成了允许在第三方网站发起的请求中携带,CSRF 攻击就是利用了 cookie 的这一“弱点”,防止 CSRF 攻击的办法已经有 CSRF token 校验和 Referer 请求头校验。为了从源头上解决这个问题,Google 起草了一份草案来改进 HTTP 协议,那就是为 Set-Cookie 响应头新增 SameSite 属性,它用来标明这个 cookie 是个“同站 cookie”,同站 cookie 只能作为第一方 cookie,不能作为第三方 cookie。
Chrome 51 开始,浏览器的 Cookie 新增加了一个SameSite属性,用来防止 CSRF 攻击和用户追踪。

SameSite 属性

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

它可以设置三个值。

  • Strict
  • Lax
  • None

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

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

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

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

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

导航到目标网址的 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 属性。

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

Set-Cookie: widget_session=abc123; SameSite=None
  • 1

下面的设置有效

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

该用哪种模式?

该用哪种模式,要看你的需求。比如你的网站是一个少数人使用的后台管理系统,所有人的操作方式都是从自己浏览器的收藏夹里打开网址,那我看用 Strict 也无妨。如果你的网站是微博,用了 Strict 会这样:有人在某个论坛里发了帖子“快看这个微博多搞笑 http://weibo.com/111111/aaaaaa”,结果下面人都回复“打不开啊”;如果你的网站是淘宝,用了 Strict 会这样:某微商在微博上发了条消息“新百伦正品特卖5折起 https://item.taobao.com/item.htm?id=1111111”,结果点进去顾客买不了,也就是说,这种超多用户的、可能经常需要用户从别的网站点过来的网站,就不适合用 Strict 了。

假如你的网站有用 iframe 形式嵌在别的网站里的需求,那么连 Lax 你也不能用,因为 iframe 请求也是一种异步请求。或者假如别的网站有使用你的网站的 JSONP 接口,那么同样 Lax 你也不能用,比如天猫就是通过淘宝的 JSONP 接口来判断用户是否登录的。

有时安全性和灵活性就是矛盾的,需要取舍。

和浏览器的“禁用第三方 cookie”功能有什么区别?

主流浏览器都有禁用第三方 cookie 的功能,它和 SameSite 有什么区别?我能总结 2 点:

  1. 该功能是由用户决定是否开启的,是针对整个浏览器中所有 cookie 的,即便有些浏览器可以设置域名白名单,那最小单位也是域名;而 SameSite 是由网站决定是否开启的,它针对的是某个网站下的单个 cookie。

  2. 该功能同时禁用第三方 cookie 的读和写,比如 a.com 发起了对 b.com 的请求,这个请求完全不会有 Cookie 请求头,同时假如这个请求的响应头里有 Set-Cookie: foo=1,foo 这个 cookie 也不会被写进浏览器里;而 SameSite 只禁用读,比如 b.com 在用户浏览器下已经写入了个 SameSite cookie foo,当 a.com 请求 b.com 时,foo 肯定不会被发送过去,但 b.com 在这个请求的响应里又返回了: Set-Cookie: bar=1; SameSite=Strcit,这个 bar 会成功写入浏览器的 cookie 里。

怎样才算第三方请求?

当一个请求本身的 URL 和它的发起页面的 URL 不属于同一个站点时,这个请求就算第三方请求。那么怎样算是同一个站点?是我们经常说的同源(same-origin)吗,cross-origin 的两个请求就不属于同一个站点?显然不是的,foo.a.com 和 bar.a.com 是不同源的,但很有可能是同一个站点的,a.com 和 a.com:8000 是不同源的,但它俩绝对是属于同一个站点的,浏览器在判断第三方请求时用的判断逻辑并不是同源策略,而是用了 Public Suffix List 来判断。

后台语言的支持程度

目前还没有哪个后台语言的 API 支持了 SameSite 属性,比如 php 里的 setcookie 函数,或者 java 里的 java.net.HttpCookie 类,如果你想使用 SameSite,需要使用更底层的 API 直接修改 Set-Cookie 响应头。Node.js 本来就没有专门设置 cookie 的 API,只有通用的 setHeader 方法,不过 Node.js 的框架 Express 已经支持了 SameSite。

参考网址 https://blog.csdn.net/yanyang1116/article/details/56511009,
https://www.jianshu.com/p/66f77b8f1759,
https://www.cnblogs.com/ziyunfei/p/5637945.html,
http://www.ruanyifeng.com/blog/2019/09/cookie-samesite.html。

原文地址:https://www.cnblogs.com/chinasoft/p/13605941.html