2491 字
约 8 分钟
16
S02-A19 · Cookie 的 `SameSite` 与跨站:登录为什么在嵌入页里失效

一个后台系统在自家域名下登录正常,被嵌到客户的 iframe 里后"登录成功但立刻掉线"。原因是 Cookie 设了 SameSite=Lax,而嵌入场景下的请求是"跨站"的,于是浏览器不带 Cookie——服务端看到"没登录",返回 401,前端跳回登录页。

这类问题的麻烦在于"它生效了但只在某些场景失效":同站访问正常、跨站/嵌入/第三方跳转全挂。这个"没有提示"的性质很重要:浏览器不会在响应里说"我拒了你的 Cookie",服务端只看到"请求没带"。于是"是前端没设"还是"浏览器拒收",需要靠 DevTools 才能区分。
而排查时你看到的日志是"没有 Cookie",而不是"Cookie 被拒"——浏览器不告诉你原因。


1. 三种 SameSite 值

值 行为
Strict 任何跨站请求都不带
Lax 顶层导航的 GET 带,其它不带
None 都带(必须配 Secure)

第二项是默认值(Chrome 80+ 起):它保证了"点链接进来"能带上 Cookie,但"嵌入/表单提交/AJAX"不带。第二项的"顶层导航"有个细节:它区分"GET 顶层导航"与"其它请求"——所以"从外部链接点进来"通常能带 Cookie,而"页面内 AJAX 到别的域"不能。

第三项的"唯一"正在变弱:第三方 Cookie 的整体收紧让 SameSite=None 也不再总有效。所以"依赖第三方 Cookie"的架构本身在退潮。
第三项是"跨站必须可用"时的唯一选择,而它有两个硬约束:必须 Secure(HTTPS),且第三方 Cookie 还受浏览器策略限制(Safari 默认阻止,Chrome 逐步收紧)。

2. "同站"的判定不是"同域"

例子 是否同站
a.example.com ↔ b.example.com 同站(同注册域)
a.example.com ↔ a.other.com 跨站
example.com:8080 ↔ example.com:9090 同站(端口不影响)
http://x ↔ https://x 同站(协议不影响 SameSite 判定)

第二项是"跨站"的常见误判:域名后缀不同就是跨站,即使它们属于同一家公司。第二项还有个"子域"细节:a.example.com 与 example.com 是同站(同注册域),所以"子域之间共享 Cookie"在 SameSite 上是成立的——前提是 Domain 设对。

第四项常被误解:SameSite 只看"注册域",与协议无关;但 Secure 属性只看协议。

3. 五种"登录失效"的场景

场景 原因
iframe 嵌入 第三方上下文,Lax 不带
跨站表单 POST 非顶层 GET,Lax 不带
支付回调跳回 跨站顶层 GET(Lax 通常可带)
移动端 WebView Cookie 策略与浏览器不同
多域名部署 同站判定不同

第一项(iframe)还有一个"双重"问题:嵌入方与被嵌入方都要允许(X-Frame-Options/CSP frame-ancestors),否则页面根本加载不出来——而"加载不出来"与"登录掉线"是两种症状。
第一项与第五项最常被误认为"后端 bug":服务端确实没收到 Cookie,但它不是"没发",而是"浏览器没带"。

"本地开发"还有一处必须显式配置,否则"本地好后端就坏了":

// 本地与生产的三个差异(容易忽略)
1 协议:本地 http,生产 https → Secure 属性在本地被拒
2 域名:localhost 与 example.com → Domain 属性不能照抄
3 端口:8080 与 80 → SameSite 不看端口,但 CORS 看 origin
// 建议:用配置文件区分,而不是"本地注释掉 Secure"

4. 四种应对

方案 代价
改 SameSite=None; Secure 需 HTTPS + 第三方 Cookie 策略允许
用 token(Header 传递) 改认证方式
同站代理(把嵌入页代理到同域) 架构改动
分区 Cookie(CHIPS) 兼容性有限

第二项是"最彻底"的:把认证凭据从 Cookie 换成 Header 里的 token第二项的代价有个具体项:"跨页持久化"——Cookie 是浏览器自动带的,而 Header token 需要前端存(localStorage/内存)并在每个请求加。于是"存哪"变成了新的安全问题(XSS 可读 localStorage)。
,于是"带不带 Cookie"不再影响认证。代价是"存储位置"与"XSS 风险"要重新考虑。

第三项的实现要点是"路径重写要彻底":页面里的绝对路径(/api/...、/static/...)都要被代理,否则"部分资源跨站加载"又会触发同样的 Cookie 问题。
第三项在"必须嵌入"的场景里实用:用同域的反向代理把第三方页面"本地化",于是同站判定成立。代价是多一层代理与路径重写。

// 三种方案的选择判据
能不能用 HTTPS + 第三方 Cookie?→ 是:SameSite=None; Secure(最省)
认证方式能否改 Header token?  → 能:换 token(最稳)
都做不到?                      → 同站代理(改动最大)

四个属性要一起看,任何一个不对都会"设了但不生效":

Set-Cookie: sid=abc123; Path=/; Domain=.example.com; Secure; HttpOnly; SameSite=None
// Path=/        路径不匹配 → 该路径拿不到
// Domain=.example.com  要共享给子域时必须显式设父域
// Secure        跨站(None)时强制要求 HTTPS
// HttpOnly      禁止 JS 读取(防 XSS 窃取,但也意味着前端拿不到)
// SameSite=None 跨站必须
手段 得到
DevTools 的 Network → Cookie 面板 请求是否带 Cookie
DevTools 的 Application → Cookies 属性与"被阻止"标记
响应头 Set-Cookie 服务端设了什么
服务端日志(Cookie 头) 实际收到什么

第二项最直接:Chrome 会在"被阻止的 Cookie"里标黄并给出原因第一项(Network 面板)的用法要具体:看"请求头里有没有 Cookie",以及"响应头里 Set-Cookie 是否被接受"。两件事都要看,因为"设了但被拒"与"根本没设"表现相同。
(如 "This cookie was blocked because it was set without SameSite=None")。

第三项要看全:Set-Cookie 里的 SameSite/Secure/Domain/Path 四项任何一个不对,都会导致"设了但不生效"。

"第三方 Cookie 政策"已经是变量,而不是常量:Safari 默认阻止、Chrome 在逐步收紧、Firefox 有分区存储。所以"在我浏览器里好用"不能作为验证结论——要按"目标用户的浏览器分布"测。

// 三种浏览器策略对"嵌入登录"的影响
Safari:第三方 Cookie 默认阻止 → 嵌入页登录基本不可用
Chrome:逐步收紧 + CHIPS(分区)→ "None"不再总是有效
Firefox:分区存储(按顶层站点隔离)→ 同一用户在不同嵌入方下是两套 Cookie
// 结论:"跨站登录"应当在设计阶段就避开,而不是上线后修

6. 排错:五种"Cookie 相关"

现象 成因 处理方向
嵌入页登录掉线 Lax 不带跨站 None; Secure 或换 token
本地正常、线上失效 线上是 HTTPS + Secure 缺失 补 Secure
子域拿不到 Cookie Domain 未设父域 设 Domain=.example.com
某路径不生效 Path 不匹配 设 Path=/
登出后仍登录 只清了前端状态 服务端也要清 Cookie

第五项(登出后仍登录)在"前后端分离"里很常见:前端清了 token,而 Cookie 还在,于是"看起来登出了",刷新又登录上。所以登出必须"服务端也清 Cookie"。
第二项很常见:本地 http://localhost 上 SameSite=None 需要 Secure,而本地没 HTTPS 时浏览器会拒——于是"本地好了、线上坏了"或反之。

最后一个"迁移"建议:从 Cookie 迁到 Header token 时,先"双读"再"单读"。也就是"服务端同时接受 Cookie 与 Bearer token",等前端全量切换后再关掉 Cookie 路径——否则"切换过程中一半用户掉线"。

// 三段式迁移(每段可回滚)
1 服务端支持 Bearer(Cookie 仍有效)
2 前端逐步切到 Bearer(灰度)
3 关闭 Cookie 认证(观察一周无异常后)
// 反例:一步切换 → 老版本前端 + 新服务端 = 全量 401

7. 边界:什么情况不必管 SameSite

场景 为什么 做法
纯同站应用(无嵌入、无跨站) Lax 足够 默认即可
用 Header token 认证 与 Cookie 无关 不管
内部工具(单一域名) 无跨站 默认
移动 App(非 WebView) 无浏览器 Cookie 不管

判断口径一句话:这个页面会不会在"另一个注册域"下被加载或提交。最后一条与"测试"有关的边界:Cookie 问题几乎无法在单元测试里覆盖,因为它依赖浏览器行为。可行的验证是"用真实浏览器在三种上下文里各测一次"(同站、跨站 iframe、跨站跳转)——十分钟能覆盖。
会(嵌入、跨站跳转、第三方登录),就必须处理 SameSite;不会(自家站点内跳转),默认值够用。


8. 自测

  1. 三种 SameSite 值的行为差别是什么?None 的两个硬约束是什么?
  2. "同站"的判定依据是什么?端口与协议分别有什么影响?
  3. 五种登录失效场景里,哪两种最常被误认为"后端 bug"?
  4. 四种应对方案里,哪一种最彻底?它的代价是什么?
  5. 为什么"本地正常、线上失效"常见于 Cookie 问题?
  6. 判断"是否需要处理 SameSite"的那个问题是什么?
S02-A19 · Cookie 的 `SameSite` 与跨站:登录为什么在嵌入页里失效
https://www.clxhxhhr.top/posts/6557/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。