SameSite 是 Cookie 的一种属性,主要用于防止跨站请求伪伪造(CSRF,Cross-Site Request Forgery)攻击。它通过控制第三方 Cookie 的发送时机,来保护用户的隐私和安全。
在深入 SameSite 之前,我们需要明确两个概念:
- 第一方 Cookie (First-party Cookie): 你当前访问的网站(地址栏里的域名)写入的 Cookie。
- 第三方 Cookie (Third-party Cookie): 页面中嵌入的其他网站(如广告、图片、iframe 插件)写入的 Cookie。
1. 核心概念:什么是"同站 (Same-Site)"?
SameSite 的"站(Site)"和我们平时说的"同源策略 (Same-Origin)"不同,它的定义更宽松。
满足以下条件的两个 URL 被认为是同站(Same-Site)的:
有效顶级域名 + 一级域名(eTLD+1) 相同
TLD是指.com、.org等。eTLD(有效顶级域名)是指像.com.cn、.co.uk这种复合域名。- 举个例子:
www.baidu.com和tieba.baidu.com属于同站 (因为都是baidu.com)。baidu.com和google.com属于跨站(Cross-Site)。a.github.io和b.github.io属于跨站 (因为github.io是一个 eTLD,它的下一级才是一级域名)。
2. SameSite 的三种策略值
SameSite 属性可以设置三个值:Strict、Lax 和 None。
① SameSite=Strict (严格模式)
这是最安全的模式。
- 规则: 任何跨站的请求(无论通过什么方式触发)都绝不发送该 Cookie。只有当你在当前域名下操作,或者地址栏域名完全一致时才会携带。
- 场景: 比如你在百度(
baidu.com)页面里点击了一个指向淘宝(taobao.com)的链接。即使你之前在淘宝登录过,由于是跨站跳转,浏览器在请求淘宝时不会 带上淘宝的StrictCookie。 - 优缺点: 防御 CSRF 效果最好,但极度影响用户体验(比如从邮件里点开一个知乎链接,进去了却发现需要重新登录)。
② SameSite=Lax (宽松模式)
这是目前主流浏览器的默认模式(平衡了安全与体验)。
- 规则: 拒绝大部分跨站 Cookie 的发送,但允许"导航到目标网址的常规安全跳转"携带 Cookie。
- 具体而言,只有满足以下两个条件时,跨站请求才会带上 Lax Cookie:
- HTTP 方法必须是安全的(通常是
GET) 。POST、PUT、DELETE等绝对不带。 - 必须是顶级导航(Top-level navigation),即该请求会导致浏览器地址栏的 URL 发生变化。
- HTTP 方法必须是安全的(通常是
- 允许带 Lax Cookie 的场景:
<a href="...">链接跳转。<link rel="prerender" href="...">预加载。window.location.replace("...")或表格的GET提交。
- 禁止带 Lax Cookie 的场景:
ajax/fetch请求。<img src="...">、<iframe src="...">、<script src="...">等隐式加载的资源。POST表单提交。
③ SameSite=None (无限制)
关闭 SameSite 限制。
- 规则: 无论同站还是跨站,任何请求都会带上 Cookie(回到了当年的传统行为)。
- 硬性前提条件: 现代浏览器(Chromium 80 及以上)规定,如果设置了
SameSite=None,就必须同时设置Secure属性 。这意味着该 Cookie 只能通过HTTPS协议传输,否则浏览器会直接拒绝写入该 Cookie。 - 场景: 专门用于第三方广告追踪、跨域共享状态、内嵌的第三方 iframe 支付插件等。
3. 三种策略的对比表格
| 请求类型 | 示例代码 | Strict | Lax (默认) | None (+Secure) |
|---|---|---|---|---|
| 链接跳转 (GET) | <a href="https://site.com"> |
❌ 不发送 | ✅ 发送 | ✅ 发送 |
| 预加载 (GET) | <link rel="prerender" href="..."> |
❌ 不发送 | ✅ 发送 | ✅ 发送 |
| 表单提交 (GET) | <form method="GET" action="..."> |
❌ 不发送 | ✅ 发送 | ✅ 发送 |
| 表单提交 (POST) | <form method="POST" action="..."> |
❌ 不发送 | ❌ 不发送 | ✅ 发送 |
| 图片/脚本加载 | <img src="...">, <script src="..."> |
❌ 不发送 | ❌ 不发送 | ✅ 发送 |
| 框架嵌套 | <iframe src="..."> |
❌ 不发送 | ❌ 不发送 | ✅ 发送 |
| 异步请求 | fetch("..."), axios.get("...") |
❌ 不发送 | ❌ 不发送 | ✅ 发送 |
4. 如何在服务器端设置?
在 HTTP 响应头(Response Headers)中,通过 Set-Cookie 来指定:
HTTP
# 严格模式
Set-Cookie: sid=abc123; SameSite=Strict; Secure
# 默认的宽松模式
Set-Cookie: sid=abc123; SameSite=Lax
# 允许第三方使用(必须带 Secure)
Set-Cookie: sid=abc123; SameSite=None; Secure
5. 总结与开发建议
- 敏感操作防范: 涉及修改用户数据、资金安全、核心业务的 Cookie,尽量设为
Strict。 - 常规会话(Session ID): 保持默认的
Lax即可,既能防御绝大多数POST/AJAX形式的 CSRF 攻击,又不会破坏正常的链接跳转体验。 - 跨域共享: 如果你的系统是分布式架构,且使用了不同的一级域名(比如微服务 A 域名是
aaa.com,B 域名是bbb.com),且需要跨域携带 Cookie,必须使用SameSite=None; Secure,并确保全站走 HTTPS。