跨域请求中的 CSRF Cookie 设置问题,往往是后端配置、浏览器安全策略和前端请求方式三方共同作用的结果。Cookie 能不能写入、能不能随请求发送,取决于 Set-Cookie 的 SameSite、Secure、Domain、Path 属性,也取决于跨域响应是否明确允许携带凭据。只要其中任何一环被忽略,表现就是登录后接口仍然返回 401 或 CSRF 校验失败。

理解 Cookie 的 SameSite 属性与跨域携带条件
SameSite 属性控制 Cookie 在跨站请求中是否被浏览器发送。SameSite=Strict 只允许同站导航携带 Cookie,SameSite=Lax 允许顶级导航携带但阻止跨域子请求,SameSite=None 则允许跨站子请求携带,但必须同时设置 Secure 标记。现代浏览器对未显式设置 SameSite 的 Cookie 默认按 Lax 处理,而 Lax 策略下,通过 fetch 或 XMLHttpRequest 发起的跨域 POST 请求不会携带该 Cookie。
跨域请求想要携带 Cookie,除了 SameSite=None; Secure,还需要后端响应头 Access-Control-Allow-Origin 不能为星号,必须指定具体来源,并设置 Access-Control-Allow-Credentials: true。浏览器只有在响应头中看到允许凭据,并且来源精确匹配时,才会把 Cookie 写入并允许前端读取响应。前端则需要显式开启 withCredentials 或 credentials: 'include',否则即使后端配置正确,浏览器也不会发送 Cookie。
还需要注意 Secure 标记在本地开发中的限制。Secure 表示 Cookie 只能在 HTTPS 连接中传输,但 localhost 通常被浏览器视为安全上下文,所以 localhost 环境可以测试 SameSite=None; Secure。但如果通过 IP 地址访问,很多浏览器不会将非 HTTPS 的 IP 地址视为安全上下文,导致 Cookie 被拒绝写入,这也是开发环境频繁遇到登录后 Cookie 丢失的原因。
后端与前端联合配置的完整方案
先看后端如何正确设置 CSRF Cookie。以 Node.js Express 为例,Set-Cookie 响应头必须包含 SameSite=None; Secure,同时 CORS 中间件要支持凭据。不能使用 Access-Control-Allow-Origin: *,而要读取请求的 Origin 头并动态反射,或者配置白名单。
const express = require('express');
const cors = require('cors');
const cookieParser = require('cookie-parser');
const app = express();
const allowedOrigins = ['https://app.ipipp.com', 'http://localhost:3000'];
app.use(cookieParser());
app.use(cors({
origin: function (origin, callback) {
// 允许无 Origin 的同源请求或白名单来源
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true
}));
app.get('/api/csrf-token', (req, res) => {
const csrfToken = require('crypto').randomUUID();
res.cookie('csrf_token', csrfToken, {
httpOnly: false,
secure: true,
sameSite: 'none',
path: '/',
maxAge: 3600000
});
res.json({ csrfToken });
});
上面的配置将 CSRF Token 写入 Cookie,并将 Token 同时返回给前端。前端在请求敏感操作时,需要把 Token 从 Cookie 中读取出来,放进 X-CSRF-Token 请求头或请求体。Cookie 本身只用于存储,真正的校验依赖后端比对请求头中的 Token 与 Cookie 中的 Token 是否一致。之所以允许 httpOnly: false,是为了让前端 JavaScript 能读取该 Cookie,如果采用服务端渲染或隐藏表单方式,也可以保持 httpOnly 以提升安全性。
前端请求必须开启 credentials。以 axios 和原生 fetch 为例:
// axios
axios.get('https://api.ipipp.com/api/csrf-token', {
withCredentials: true
}).then(res => {
const token = res.data.csrfToken;
return axios.post('https://api.ipipp.com/api/update-profile', {
nickname: 'new-name'
}, {
withCredentials: true,
headers: { 'X-CSRF-Token': token }
});
});
// fetch
fetch('https://api.ipipp.com/api/csrf-token', {
credentials: 'include'
}).then(res => res.json()).then(data => {
return fetch('https://api.ipipp.com/api/update-profile', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': data.csrfToken
},
body: JSON.stringify({ nickname: 'new-name' })
});
});
如果使用 Cookie 存储 CSRF Token 并采用双重提交校验,后端不需要在服务端保存 Token,只需校验请求头中的 X-CSRF-Token 与 Cookie 中的 csrf_token 是否一致即可。攻击者无法读取受害者浏览器中的 Cookie 值,也无法在跨站请求中自定义该请求头,因此这种方案能有效阻断 CSRF。
但如果接口只依赖 Cookie 中的会话标识而不做 CSRF Token 校验,即使 Cookie 正确设置,仍然存在 CSRF 风险。跨域携带 Cookie 是 CSRF 攻击的前提,因此 SameSite=None 会让 Cookie 更容易被跨站请求携带,必须配合 CSRF Token 或同源校验来降低风险。
排查清单与本地开发常见陷阱
当 Cookie 未能正确设置时,可以按以下顺序排查。首先打开浏览器开发者工具,查看 Network 面板中响应头的 Set-Cookie 是否出现,以及 Set-Cookie 前面是否有黄色警告图标。Chrome 会在 Cookie 被拒绝时给出明确原因,例如 this Set-Cookie was blocked because it had the Secure attribute but was not received over a secure connection,或者 blocked due to user preferences。
其次检查 CORS 响应头。浏览器跨域请求要求预检或实际响应中同时出现 Access-Control-Allow-Origin、Access-Control-Allow-Credentials、Access-Control-Allow-Headers 和 Access-Control-Allow-Methods。带凭据请求中,Access-Control-Allow-Origin 不能是星号,也不能使用多个来源,必须是请求 Origin 的精确值。若使用反向代理统一处理 CORS,需确认代理不会覆盖后端的 Set-Cookie 或 Access-Control-Allow-Credentials。
本地开发还有一个高频问题:前端使用 localhost,后端使用 127.0.0.1,或者前端使用局域网 IP,后端使用 localhost,虽然 IP 对应同一台机器,但浏览器仍视为跨站请求。此时如果 SameSite=None 和 Secure 未正确设置,Cookie 同样不会写入。建议在开发环境统一访问地址,或者使用 localhost 作为 API 域名,并为开发环境生成自签名 HTTPS 证书。
最后要注意第三方 Cookie 的逐步淘汰。Safari 和 Firefox 已经默认阻止第三方 Cookie,Chrome 也在推进相关策略。如果业务依赖跨站子请求携带身份 Cookie,必须关注 Partitioned Cookie 或令牌方案。若只是前后端分离的跨域场景,但前后端属于同一组织,可以考虑通过反向代理把前端和 API 放在同一站点下,从根源上避免第三方 Cookie 限制。
跨域请求CSRF CookieSameSite修改时间:2026-10-05 02:31:50