Iframe 与 SameSite:Iframe 中设置 cookie 失败的原因及解决方案

5,214次阅读
没有评论

共计 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 扩展参考

正文完
 0
任侠
版权声明:本站原创文章,由 任侠 于2023-10-30发表,共计2563字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)
验证码