共计 2563 个字符,预计需要花费 7 分钟才能阅读完成。
提醒:本文最后更新于2025-07-07 14:30,文中所关联的信息可能已发生改变,请知悉!
从 Chrome 51 开始,其针对 Cookie 新增加了一个 SameSite 属性,用于防止 CSRF 攻击和用户追踪等。
而从 Chrome 80 开始 SameSite 默认值发生改变,由 None
变为 Lax
,由此导致许多在网页上跳转跨站的网站页面时不会携带 Cookie,造成登录态失效等一系列问题。此外,在基于 https 的 iframe 页面中设置 cookie 也会失效。
1 跨站嵌入的 Iframe 页面中设置 cookie 失效的问题
例如在页面 https://abc.lzw.me
中存在如下 iframe
:
<iframe src="https://xxx.lzw.me"></iframe>
当 htts://xxx.lzw.me
载入时,存在使用 JavaScript 设置 cookie 的操作。在之前其方法可能是这样的:
function setCookie(name, value, seconds = 0, path = '/') { | |
let expires = ''; | |
if (seconds) { | |
const date = new Date(); | |
date.setTime(date.getTime() + seconds * 1000); | |
expires = `; expires=${date.toGMTString()}`; | |
} | |
document.cookie = `${name}=${escape(value)}${expires}; path=${path}`; | |
} |
上面的 setCookie
方法在页面单独从浏览器访问时执行正常,但在 Chrome 80 以后,当从 iframe 中加载时则会无效,并不会改变 docment.cookie
的值。在 iframe 中访问第三方页面时,若需要成功的设置 cookie 并在后续的 xhr 请求中携带它,则必须设置 SameSite=none
,并设置 Secure
以只允许在 https 形式的请求中可携带。以下是一个可以设置生效的简单示例:
document.cookie = 'token=467d1510-xxxx-xxxx-xxxx-73852620effa1; path=/; Secure; SameSite=None';
于是我们可以更新 setCookie
方法,参考如下:
function setCookie(sKey: string, sValue: string | number, vEnd?: number | string | Date, sPath?: string, sDomain?: string, bSecure?: boolean) { | |
if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) return false; | |
let sExpires = ''; | |
if (vEnd) { | |
switch (vEnd.constructor) { | |
case Number: | |
sExpires = vEnd === Infinity ? '; expires=Fri, 31 Dec 9999 23:59:59 GMT' : `; max-age=${vEnd}`; | |
break; | |
case String: | |
sExpires = `; expires=${vEnd}`; | |
break; | |
case Date: | |
sExpires = `; expires=${vEnd.toUTCString()}`; | |
break; | |
} | |
} | |
document.cookie = [ | |
`${encodeURIComponent(sKey)}=${encodeURIComponent(sValue)}`, | |
sExpires, | |
sDomain ? `domain=${sDomain}` : '', | |
sPath ? `; path=${sPath}` : '', | |
bSecure ? 'SameSite=None; Secure' : '', | |
] | |
.Filter(Boolean) | |
.join('; '); | |
return true; | |
} |
需要注意的是,通过 JavaScript 设置的 Cookie 不能包含 HttpOnly 标志,即使包含了也是无效的。
2 Iframe 中服务端返回设置的 cookie 无效问题
当在 iframe 中请求服务端认证接口并成功后,接口同时设置了认证 token 至 cookie 中。这在页面单独访问时一切正常,但当以 iframe 的形式被嵌入存在跨域的页面中时,则 cookie 设置会失效。
这也是因为没有明确的指定 SameSite
属性值。以 Node.js 开发的后端服务为例,在此之前的设置方式可能是这样的:
const https = require('https'); | |
const fs = require('fs'); | |
https | |
.createServer( | |
{ | |
key: fs.readFileSync(__dirname + '/key.pem'), | |
cert: fs.readFileSync(__dirname + '/cert.pem'), | |
}, | |
(req, res) => { | |
if (req.url === '/test-setcookie') { | |
res.setHeader('Set-Cookie', 'token=467d1510-xxxx-xxxx-xxxx-73852620effa1; httpOnly'); | |
res.end('test cookie'); | |
} | |
} | |
) | |
.listen(443, '0.0.0.0'); |
在需要支持 iframe 嵌入的场景下,应修改为下面这种方式:
if (req.url === '/test-setcookie') { | |
res.setHeader('Set-Cookie', 'token=467d1510-xxxx-xxxx-xxxx-73852620effa1; httpOnly; Secure; SameSite=None'); | |
// 从请求头获取其来源站点识别是否为跨域请求,并作相关逻辑识别与约束 | |
if (req.headers.origin) { | |
// 注意这里不可设置为 *,必须指定为具体的域名,否则会报错 | |
res.setHeader('Access-Control-Allow-Origin', req.headers.origin); | |
// 允许跨域请求携带 Cookie | |
res.setHeader('Access-Control-Allow-Credentials', 'true'); | |
} | |
res.end('test cookie'); | |
} |
3 扩展参考
相关文章:











