HTTP: Cookie: SameSite

 20th August 2020 at 2:19pm

Cookie 中 SameSite 指令的引入,是为了解决 CSRF 问题。

SameSite 做的是当用户在访问 A 网站中,对 B 网站发起请求时的 cookie 控制。比如用户在访问恶意网站 https://evil.example 时,页面中含有这样一张图片:

<img src="https://bank.example/transfer?from=alex&to=eva" />
<a href="https://bank.example/transfer?from=alex&to=eva">Click Me!</a>

此时浏览器会向这个 URL 发起请求。那它应该带上 bank.example 网站的 cookie 吗?如果用户在 bank 网站上已经登录,那 cookie 代表了用户的登录状态,此时访问转帐链接会触发真正的转帐,导致用户被黑。SameSite 指令可以控制是否带 cookie 的行为。它有 三个取值

None
The browser will send cookies with both cross-site requests and same-site requests.
Strict
The browser will only send cookies for same-site requests (requests originating from the site that set the cookie). If the request originated from a different URL than the URL of the current location, none of the cookies tagged with the Strict attribute will be included.
Lax
Same-site cookies are withheld on cross-site subrequests, such as calls to load images or frames, but will be sent when a user navigates to the URL from an external site; for example, by following a link.

结合上面的 evil 网站例子,如果 bank 网站上的 cookie 的 SameSite 值被设置成这些场景时,访问 evil 网站会有怎样的行为:

  • None:浏览器 仍然会 将 bank 的 cookie 发送。这会引起用户被黑。
  • Strict:浏览器 不会 将 bank 的 cookie 发送;即使用户点击 "Click Me!" 链接,浏览器也 不会 发送 bank 的 cookie
  • Lax:浏览器 不会 将 bank 的 cookie 发送;但是用户点击 "Click Me!" 链接,浏览器 发送 bank 的 cookie

Lax 像是一个 relax 版的 strict。这篇 文章 总结了不同情况下 cookie 是否会被带上的表格:

请求类型代码示例哪些情况下 cookie 会被发送
Link<a href="..."></a>None, Lax
Perender<link rel="prerender" href=".."/>None, Lax
Form GET<form method="GET" action="...">None, Lax
Form POST<form method="POST" action="...">None
iframe<iframe src="..."></iframe>None
AJAX$.get("...")None
Image<img src="...">None

Lax 是理论设计与工程实践的优雅结合。如果将浏览器默认的 SameSite 策略改成 Strict,一定会引起很多老网站无法正常运行。但是设成 Lax 的话,即解决了大部分危险场景,又使得相对安全的场景(访问一个链接、提交一个 GET 请求到 Form 表单等)不受影响。

在编写这篇文章的 2020 年 4 月,当一个 cookie 没有被指定 SameSite 属性时,大部分浏览器默认的实现都是 None。Chrome 80 开始将此值默认设置为 Lax。同时并非所有浏览器都支持 SameSite 指令。参考 这里

Neither Strict nor Lax are a complete solution for your site's security. Cookies are sent as part of the user's request and you should treat them the same as any other user input. That means sanitizing and validating the input. Never use a cookie to store data you consider a server-side secret. - https://web.dev/samesite-cookies-explained/

什么场景 SameSite 为 None 是适用的?

比如你想将一个 YouTube 视频,以 iframe 播放器形式嵌入到你的网站中。如果 YouTube 设置了 Lax / Strict,会导致用户在你的网站看到此视频时,他在 YouTube 上的已登录状态并不会体现出来。他像是未登录用户,无法点击 iframe 播放器中的「Watch Later」按钮。

Reference