返回探索
资讯VibeFix 编辑部更新于 2026年11月5日

打开一条 ChatGPT 分享链接,账号就归别人了:MathLive 监听器加 YouTube 回调的连环漏洞

2026 年 10 月 28 日,Parameter Labs 公开一条 ChatGPT 账号接管链:图表加载的 MathLive 监听器不验消息来源,HTML 预览借它把代码写进主页面,再用 YouTube 回调绕过 CSP。打开分享链接 4.5 秒后,攻击者就能以你的身份读私人对话。OpenAI 10 月 26 日已修复,赏金 5000 美元。

Parameter 研究者实测截图:ChatGPT 页面底部出现“XSS confirmed on https://chatgpt.com”横幅

先说判断:这不是又一个“别点陌生链接”的老生常谈,而是一次教科书级的接缝处失守

Parameter 研究者实测截图:ChatGPT 页面底部出现“XSS confirmed on https://chatgpt.com”横幅

2026 年 10 月 28 日,安全研究团队 Parameter Labs 公开了一篇名为《Do the Math: How We Took Over ChatGPT Accounts》的文章。它的结论一句话就能让人坐直:攻击者做一个普通的 ChatGPT 对话,用正常分享功能发一条公开链接,收到链接的人只要打开它,攻击者的代码就能以 chatgpt.com 的身份在他已登录的会话里运行——列出他的私人对话、读出从未分享过的内容、把数据发到攻击者的服务器。整个过程里,受害人不需要安装任何东西,不需要加入任何工作区,不需要粘贴代码,甚至不需要打开开发者工具。在最顺畅的复现版本里,打开链接是受害人唯一的动作,4.5 秒后,攻击代码就已经跑起来了。

好消息是:这件事已经修好了。OpenAI 在 10 月 26 日确认两个前端版本都堵住了这条链路,并为此支付了 5000 美元赏金。坏消息是:漏洞的根子不在某个低级错误上,而在五个各自看起来都合理的设计拼在一起之后才露出来。我的判断是,这篇文章值得 VibeFix 的读者认真读一遍——不是因为 ChatGPT 出了漏洞,而是因为它暴露的是一类每个做 AI 应用、做分享功能、做预览沙箱的开发者都会踩的坑:防线不是被正面攻破的,是从接缝处漏过去的。

时间线:13 天从发现到修复,升级只用了一天

先把 Parameter 公布的时间线摆出来,因为它本身就是判断这件事严重程度的标尺。10 月 13 日,研究团队确认 MathLive 的消息转 HTML 路径在 ChatGPT 生产代码上真实存在;10 月 14 日,他们通过正常界面复现了整条链,把一个弹窗升级成任意 JavaScript 执行;10 月 16 日,他们在两个独立账号、工作区之间确认了公开分享链接的投递能力,只需点一次预览按钮;10 月 17 日,他们确认自动预览版本在硬刷新后也能复现。

10 月 18 日,报告提交给 OpenAI 的漏洞赏金计划;10 月 19 日,补上了“可控的私人对话泄露”验证;10 月 21 日,OpenAI 复现了问题,严重程度从最初的 P2 升到 P1;10 月 22 日,一个前端版本修好,另一个还在更新;10 月 23 日,CSP 改动堵住了链路的一部分;10 月 26 日,OpenAI 确认两个前端都拒绝了报告里用到的跨帧 MathLive 消息,标记为已解决,赏金总额 5000 美元。

从上报到修复只用了 8 天,从 P2 升到 P1 只用了一天——OpenAI 的响应速度本身就说明了这件事的分量。更值得注意的是修复过程的分段:两个前端版本不是同时修好的,中间有一段时间一个修好了、另一个还在等更新,CSP 改动先顶上了一部分。这种“分批修复、纵深防御先顶着”的节奏,是真实世界里修高危漏洞的常态,也值得做产品的团队记住:没有哪一次修复是原子完成的,中间态本身就是风险窗口。

攻击链拆解:五步,每一步单独看都“没问题”

整条链的起点是两个看起来人畜无害的功能拼在同一个对话里:一个交互式图表,和一个 HTML 预览。ChatGPT 可以把 HTML 代码块变成一个能跑的小网页,因为代码可能是任意的,ChatGPT 把它放在一个受限的框架里、跑在另一个不同的网址上。这个隔离一开始确实起作用:预览里的代码读不到父页面的 document。到这里为止,一切正常。

第一步,图表悄悄装了一个监听器。对话里的交互式图表会在主页面加载一个叫 MathLive 的数学输入库。画图只是它的本职工作,附带效果是:它还在 ChatGPT 主页面上装了一个监听器,接收与虚拟键盘相关的消息。浏览器里不同框架之间通信用的 API 叫 postMessage,可以把它理解成一个投信口:受限的预览进不了主页面,但能往里投一封信。投信口本身没问题,前提是收信的人要核对寄信人是谁。

第二步,监听器不核对寄信人。MathLive 的顶层控制器用的来源策略是 none,也就是说,它接受来自任何网址的消息。它接受一条修改虚拟键盘布局的消息,而键盘布局字段里允许放 HTML。于是局面变成了:图表装好了收信口,预览成了一个不受信任的寄信人,两个互不相干的功能意外地搭出了一条穿过沙箱的路。预览发了两条消息:第一条用攻击者控制的 HTML 替换键盘布局,第二条让 MathLive 显示键盘,布局随即被渲染出来。

第三步,innerHTML 把 HTML 写进了真正的 ChatGPT 页面。键盘打开时,MathLive 把布局拼成 HTML 字符串,赋给了主页面里的 innerHTML。一个叫 createHTML 的辅助函数看起来像是在做净化,实际上它只是把字符串标记为可信、原样放行。Script 标签经由 innerHTML 插进去不会自动执行,所以这一步还拿不到代码执行权——但已经拿到了在受信任页面里写 HTML 的能力。

第四步,一个没有 sandbox 属性的 iframe 完成了身份转换。攻击者插进一个用 srcdoc 属性携带 HTML 的 iframe:srcdoc 让一个内嵌框架直接把 HTML 写在属性里。关键在于,MathLive 是把这个框架插进 ChatGPT 主文档的,而且没有加 sandbox 属性。浏览器因此给了它两样东西:第一,srcdoc 创造了一个全新的文档,外部脚本可以在里面执行;第二,因为是主页面创建的框架,浏览器给了它 chatgpt.com 的身份。框架里的代码从此可以访问父页面——沙箱的墙还在,但攻击者已经让受信任的一方亲手开了一扇没有锁的门。

第五步,借 YouTube 的 oEmbed 接口绕过 CSP。ChatGPT 的内容安全策略(CSP)正确地拦住了内联 JavaScript,但它允许加载来自 YouTube 的脚本。YouTube 的 oEmbed 接口支持 JSONP 风格的回调参数:它会先把回调放进响应里,再返回校验错误。攻击者的回调是 globalThis.__marker=31337;void,末尾的 ;void 把 YouTube 追加的对象变得无害。既然 YouTube 本来就在 CSP 的白名单上,浏览器就把这段响应当作脚本加载并执行了回调。最终的回调有 5533 个字符,整个 YouTube 链接 7309 个字符,算上 script 标签 7337 个字符,整个 iframe 标记 7390 个字符——每一层转义都被研究团队精确地算过。

沙箱没破,桥断了:最值得玩味的一点

Parameter 的文章里有一句最值得划线的话:预览沙箱本身是起作用的。预览里的代码确实读不到主页面,隔离机制没有失效。出问题的是主页面里的另一个组件愿意接受它的指令。换句话说,墙没倒,是有人从墙里递了一把梯子出去。

研究团队把根因归结为三个设计决定:第一,MathLive 接受了一条会改变状态的消息,却没有证明发消息的框架是谁;第二,消息格式允许把原始 HTML 当作键盘布局数据传进来;第三,这些 HTML 可以创建一个继承 chatgpt.com 身份的无沙箱文档。YouTube 那一步只是绕过 CSP 的变通办法,不是根因——就算只堵住 YouTube 那个接口,不受信任的预览仍然能往 ChatGPT 页面里插 HTML。

这个区分非常重要,因为它决定了修复的优先级。OpenAI 最终修的是跨帧 MathLive 消息本身,而不是去追 YouTube 的回调接口。做安全的人常说的一句话在这里得到了完美验证:CSP 是纵深防御的后备控制,不是攻击者拿到 HTML 之后的唯一屏障。一旦不可信的标记进入了主站,单靠 CSP 是兜不住的。

“只需打开链接”:为什么这次的投递方式格外危险

光有漏洞链还不够,还得能把它送到受害人面前。研究团队一开始的版本有个短板:它用的是研究者自己控制的 ChatGPT 应用当发送方,受害人得先连接或调用那个应用才行,真实攻击里这一步很可疑。后来他们试了 HTML 附件:ChatGPT 会在同一个沙箱预览里打开附件,附件能发恶意 MathLive 消息,但公开分享后图表不见了——而图表是必需的,因为只有它会加载 MathLive、装好那个监听器。

突破口是把两个部件直接放进一次普通的公开对话:一个真正的图表初始化 MathLive,一个由助手生成的 HTML 预览发送两条消息。生成的 /share/<id> 链接完整保留了整条链。攻击者递送它的方式,和发任何一条分享链接没有区别:邮件、聊天、工单、社交媒体帖子都行。最稳定的复现里,受害人打开分享、点一次 ChatGPT 正常的预览按钮即可;后来他们把 HTML 块标记为可自动预览,打开链接就成了受害人唯一的点击。研究团队在另一个账号里复现了自动路径三次,包括硬刷新之后,弹窗大约在 4.5 秒后出现。图表和预览是独立加载的,偶尔会出现消息发早了、MathLive 还没准备好的竞态,最终的载体用有限时间内的重试解决了这个问题,主页面里的一个守卫确保了私人对话验证和对外请求只跑一次。

拿到 chatgpt.com 身份的代码能做什么?它读取页面里已有的会话信息,取出会话 token 和账号 ID,然后直接调用 ChatGPT 的对话接口:先是 GET /backend-api/conversations 列出最近的对话,再用 GET /backend-api/conversations/<id> 读出某一条私人对话的内容——那条对话和恶意分享毫无关系,从未分享给攻击者。接着,有效载荷通过一次普通的外部图片请求,把一小段响应发到研究团队控制的服务器。研究团队刻意只发了很小一部分内容,但注入的代码已经拿到了完整的认证响应,漏洞的影响范围并不限于那一小段。文章写得很直白:因为代码跑在 Web 应用的认证上下文里,任何通过前端接口暴露的数据和操作,都在这个安全边界之内——本质上,攻击者可以冒充受害人做任何事。

5000 美元便宜吗?Simon Willison 说:有点便宜

赏金数字本身也成了一个话题。Simon Willison 在 10 月 31 日的博客里转发了这篇研究,评价是:OpenAI 修好了漏洞、付了 5000 美元赏金,“我觉得这个数对于这么严重的漏洞来说有点便宜了”。他的判断不难理解:一条链接、一次点击、4.5 秒,就能完整接管一个账号的私人对话——按漏洞赏金市场的行情,这个级别的链路通常不止这个价。

但赏金数字不是这篇文章的重点。更值得记的是 Parameter 文章里没有写的东西:文章没有提到野外利用案例,也没有 CVE 编号。这是一次负责任的披露——10 月 18 日上报、10 月 26 日修复、10 月 28 日公开,中间没有公开细节,修复完成才放出全文。对普通用户来说,结论很简单:补丁是服务端下的,不需要你做什么。如果你 10 月底之前点开过可疑的 ChatGPT 分享链接,那是另外一回事,但就这篇文章披露的信息而言,没有证据表明它被大规模利用过。

还有一个后续值得提一句:据后续报道,MathLive 0.111.2 版本已经给 postMessage 加上了同源校验,堵住的不只是 ChatGPT,而是所有内嵌这个库的应用。这说明漏洞的根子确实在库的默认行为上——MathLive 接受任意来源消息的设定,任何用了它的产品都会继承。

对 vibe coding 开发者的四条教训

ChatGPT 的 HTML 预览,本质上就是现在最流行的 artifacts 模式:模型写一段 HTML,平台把它跑在一个沙箱 iframe 里,变成一个能交互的小应用。Vibe coding 社区每天都在生产这种东西:有人用 Claude、v0、Lovable 生成小工具,有人把 ChatGPT 预览里的页面分享给朋友、同事、客户。Parameter 这次撕开的口子,恰好就长在这个模式最核心的假设上——“沙箱里的代码是隔离的”。

第一条教训,写给所有用 postMessage 的人:接收方必须同时验证 event.origin 和 event.source。只验 origin 不够,只验 source 也不够——Parameter 文章的防御清单第一条就是这两项都要验。很多教程只写一半,生产代码照抄就埋了雷。今晚就可以对着自己的代码库扫一遍:凡是 addEventListener('message') 的地方,问一句“它验了谁发的吗”。

第二条,跨窗口的设置里不要放原始 HTML。MathLive 的键盘布局字段允许 HTML,是这条链上最不该存在的一环。任何要在两个框架之间传的数据,都应该先想清楚它最终会被怎么渲染——如果终点是 innerHTML,那这个字段从设计上就是错的。vibe 出来的小应用里,凡是“把用户输入拼进 HTML”的地方,都值得用同样的眼光重新看一遍。

第三条,动态创建的文档要一直带着沙箱。srcdoc iframe 没有 sandbox 属性,是攻击者拿到 chatgpt.com 身份的关键一步。原则很简单:只要文档内容可能被不可信的框架影响,它就应该始终处于沙箱之中。给 iframe 加 sandbox 属性成本几乎为零,漏加一次的代价可能是整个账号。

第四条,CSP 是兜底,不是正门。YouTube 的 oEmbed 回调能执行,是因为 CSP 白名单里有 youtube.com——而白名单里有大站,是再正常不过的配置。指望 CSP 在 HTML 已经进入主站之后力挽狂澜,是不现实的。真正的安全边界要画在“不可信内容能不能进入主站”这一步,而不是“进来之后还能不能跑脚本”这一步。

最后还有一条更宽泛的教训:跨功能测试。预览是隔离的,图表能加载数学键盘,框架之间能传消息,CSP 放行了主流媒体,公开对话可以分享——单独看每一个功能都合理,漏洞只在它们拼成一页的时候才出现。测试的时候只测单个功能、从来不测功能组合,是大多数团队的盲区。Parameter 这篇文章的标题就点明了这一点:Do the Math,算算账——把几个“各自合理”加在一起,答案可能完全出乎意料。

小结

Parameter Labs 用 13 天走完了一条从发现到公开的完整链路:10 月 18 日上报,10 月 21 日升为 P1,10 月 26 日修复,10 月 28 日公开。而对所有在做分享功能的人来说,这篇文章还有一句潜台词:公开链接本身就是攻击面,分享出去的那一刻,它就不再是你能控制的东西了。

攻击本身由五个环节组成——MathLive 不验来源的监听器、允许 HTML 的键盘布局、innerHTML 渲染、无沙箱的 srcdoc iframe 继承 chatgpt.com 身份、YouTube oEmbed 回调绕过 CSP——每一步单独看都合理,拼在一起就是一次“打开链接即接管账号”的完整攻击。对普通用户,补丁已下、无需操作;对开发者,这篇文章留下的四条教训值得贴在墙上:验 origin 也验 source,别在跨窗口消息里放原始 HTML,动态文档永远带着沙箱,CSP 只是兜底。沙箱没有破,破的是沙箱与主页面之间的那座桥——而桥,往往才是防守最薄弱的地方。

阅读 1评论 0

评论 (0)

ME
0/1000
评论加载中...

原始来源

浏览项目广场发布你的项目

相关文章