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 指令。参考 这里。
什么场景 SameSite 为 None 是适用的?
比如你想将一个 YouTube 视频,以 iframe 播放器形式嵌入到你的网站中。如果 YouTube 设置了 Lax / Strict,会导致用户在你的网站看到此视频时,他在 YouTube 上的已登录状态并不会体现出来。他像是未登录用户,无法点击 iframe 播放器中的「Watch Later」按钮。