5月28日 02:07

Cookie 在跨域场景下如何使用?需要注意哪些问题?

核心回答

Cookie 默认受同源策略约束,只在同源请求中发送。要实现跨域携带 Cookie,需要前后端同时配置:前端设置 credentials: 'include',后端返回 Access-Control-Allow-Credentials: trueAccess-Control-Allow-Origin 不能为 *。此外,Chrome 80+ 默认将 SameSite 设为 Lax,跨站场景下必须显式设置 SameSite=None; Secure

浏览器同源策略要求协议、域名、端口三者完全一致。Cookie 的"源"判断比脚本更宽松——它只看 Domain 和 Path,不区分端口和协议。但跨域请求(如 frontend.comapi.com 发请求)默认不会携带 Cookie,这是浏览器的安全行为。

前端:声明携带凭证

javascript
// fetch 方式 fetch('https://api.example.com/data', { credentials: 'include' // 跨域时发送 Cookie }); // XMLHttpRequest 方式 const xhr = new XMLHttpRequest(); xhr.withCredentials = true; xhr.open('GET', 'https://api.example.com/data'); xhr.send();

credentials: 'include' 表示无论同源还是跨域都发送 Cookie。如果只想同源发送,用 'same-origin'

后端:允许接收凭证

javascript
// Express + cors 中间件 app.use(cors({ origin: 'https://frontend.example.com', // 必须是具体域名,不能是 * credentials: true })); // 设置 Cookie res.setHeader('Set-Cookie', [ 'token=xyz; HttpOnly; Secure; SameSite=None; Path=/' ]);

三个关键约束:

  • Access-Control-Allow-Credentials: true,否则浏览器忽略响应中的 Cookie
  • Access-Control-Allow-Origin 必须是具体域名,* 会导致浏览器拒绝携带 Cookie 的请求
  • Cookie 本身需要 SecureSameSite=None(见下文)

SameSite 属性详解

SameSite 是 Cookie 最重要的跨域相关属性,控制 Cookie 在跨站请求中是否发送:

行为典型场景
Strict仅同站请求发送,跨站导航也不发送银行、支付等高安全场景
Lax(Chrome 80+ 默认)同站请求 + 顶级导航的 GET 请求发送大多数场景的默认选择
None跨站请求也发送,必须搭配 Secure跨域 API 认证、SSO

Chrome 80 之前 SameSite 默认是 None,之后改为 Lax。这意味着如果你的 Cookie 没有显式声明 SameSite,跨站的 POST 请求、iframe 内请求、Ajax 请求都不会携带 Cookie。

实际影响:如果你的前端部署在 app.com,API 部署在 api.com,这两个属于跨站(cross-site),必须设置 SameSite=None; Secure

Domain 属性实现子域共享

如果只是子域名之间共享 Cookie(如 a.example.comb.example.com),不需要走 CORS,用 Domain 属性即可:

javascript
document.cookie = "token=xyz; Domain=.example.com; Path=/";

设置 Domain=.example.com 后,所有子域名都能读取该 Cookie。注意 Domain 只能设为当前域名的父域或自身,不能跨顶级域。

主流浏览器正在逐步淘汰第三方 Cookie:

  • Safari:早已默认阻止所有第三方 Cookie(ITP 策略)
  • Chrome:从 Chrome 115 开始为第三方 Cookie 引入 Storage Access API 限制,计划全面淘汰(时间线多次调整,目前仍在推进)
  • Firefox:默认阻止已知跟踪器的第三方 Cookie(ETP 策略)

替代方案正在发展中:

  • Cookie Partitioning(CHIPS):通过 Partitioned 属性让第三方 Cookie 按顶级站点隔离,Chrome 已开始支持
  • Storage Access API:允许用户授权特定第三方访问 Cookie
  • First-Party Set:同一组织下的域名声明为第一方集合

如果你的业务依赖第三方 Cookie(如 SSO、嵌入式登录),需要尽早关注这些变化。

跨域请求如果使用了非简单方法或自定义 Header,浏览器会先发 OPTIONS 预检请求。预检请求本身不会携带 Cookie,但服务器需要在预检响应中包含 Access-Control-Allow-Credentials: true,否则后续的实际请求携带的 Cookie 会被浏览器拒绝。

javascript
// 预检请求的响应也需要配置 app.options('/api/*', cors({ origin: 'https://frontend.example.com', credentials: true, methods: ['GET', 'POST', 'PUT'], allowedHeaders: ['Content-Type', 'Authorization'] }));

安全防护要点

跨域 Cookie 场景下安全风险更高,务必注意:

  1. 必须设置 HttpOnly:防止 XSS 窃取 Cookie
  2. 必须设置 SecureSameSite=None 强制要求,确保只在 HTTPS 下传输
  3. 验证 Origin 和 Referer:后端应校验请求来源,防止非法域名的跨域请求
  4. CSRF 防护:即使有 SameSite,仍建议配合 CSRF Token,因为 SameSite=Lax 对顶级导航 GET 请求仍会放行
  5. 设置合理的过期时间:避免长期有效的 Cookie 被盗用

面试追问方向

  • SameSite 从 None 改为 Lax 后,哪些请求受影响? —— 跨站 POST、iframe 内请求、subresource 请求不再携带 Cookie,但顶级导航的 GET(如 <a> 链接点击)仍会发送。
  • CORS 为什么不允许 Access-Control-Allow-Origin: * 搭配 credentials? —— 因为 * 表示任何域名都可携带凭证访问,这等于完全绕过了同源策略的保护,浏览器的安全模型不允许。
  • Cookie 的 Domain 和 CORS 的 Origin 有什么区别? —— Domain 控制 Cookie 的作用域(哪些域名能访问),Origin 是请求头中的来源标识。两者判断"同源/跨站"的标准不同:Cookie 看注册域名(eTLD+1),CORS 看协议+域名+端口。
标签:Cookie