rrweb 沙箱化重建(Sandboxed Rebuild):用 iframe 沙箱强制浏览器重放安全边界
前端可观测性开发工具【免费下载链接】rrwebrecord and replay the web项目地址https://gitcode.com/gh_mirrors/rr/rrweb点击查看免费下载本指南详解 rrweb 项目中一项关键安全架构决策——rrweb-snapshot.rebuild()默认拒绝在不受保护的浏览器文档中重建快照转而依靠 iframe sandbox 作为浏览器强制执行的脚本执行边界。读完本文你将理解该决策的来龙去脉、rebuild()守卫的具体实现原理、createSandboxedIframe()/rebuildIntoSandboxedIframe()安全辅助 API 的用法以及UNSAFE_replayCanvas显式不安全路径的迁移方式。决策背景直接调用rebuild()带来的执行漏洞rrweb 的序列化设计见 serialization.md遵循去脚本化de-scripting原则重放时不执行录制页面中的任何 JavaScript而是通过快照还原其视觉结果。例如script标签会被改写为noscript标签。然而脚本化行为远不止script标签一种——内联事件处理程序、javascript:URL、表单提交、SVG 中的可执行内容等都属于脚本化行为而这些并不能通过简单改写全部消除。正如仓库中 docs/sandbox.md 所记录的核心结论通过过滤filtering方式清除这些脚本永远不是完整方案一旦有脚本漏网并被执行就可能造成不可逆的意外后果。因此我们使用 HTML 提供的 iframe sandbox 特性做浏览器级别的限制。问题在于rrweb的重放器replayer一直遵守这一约定会先创建sandboxallow-same-origin的顶层 iframe 再重建快照但对外暴露的底层 APIrrweb-snapshot.rebuild()可以接收调用方提供的任意Document包括页面顶层 document。社区问题rrweb-io/rrweb#1817报告当调用方直接对未沙箱化的浏览器文档调用rebuild()时精心构造的重放内容可以执行脚本。这就是 0001-require-sandboxed-browser-rebuilds.md 这条 ADR架构决策记录要填补的缺口。核心决策用沙箱强制执行安全而非扩大净化范围ADR 给出的决策明确且克制rrweb-snapshot.rebuild()将默认拒绝在不受保护的浏览器文档中重建浏览器重放安全由iframe 沙箱强制执行而不是试图净化每一个可执行的 DOM、URL、CSS、SVG 或事件处理程序向量有意在不受保护的浏览器文档中重建的调用方必须使用显式的不安全 opt-out不可信的重放数据应使用沙箱化 iframe 目标或安全辅助 API。该决策刻意将修复保持在结构性层面不扩展属性或 URL 净化逻辑那是无底洞也不重新设计基于独立源或消息传递的渲染器而是把边界判定交给浏览器原生沙箱机制。完整的目标与非目标清单记录在设计文档 2026-05-18-sandboxed-rebuild-design.md 中其中明确将不尝试净化所有可执行 HTML/SVG/CSS/URL/事件处理器向量列为非目标。守卫实现rebuild()如何判定目标文档是否受保护判定函数与错误信息守卫逻辑全部集中在 packages/rrweb-snapshot/src/rebuild.ts 中由三个部分组成错误信息常量rebuild.ts#L92-L93const REBUILD_TARGET_ERROR rrweb-snapshot.rebuild() cannot rebuild into an unprotected browser document. Use rebuildIntoSandboxedIframe() or set UNSAFE_allowUnprotectedRebuild: true only when you accept the script-execution risk.;沙箱 token 校验rebuild.ts#L157-L171目标文档所在的 frame 必须是IFRAME且其sandboxtoken 集合恰好等于[allow-same-origin]——多一个allow-scripts、allow-forms或少一个allow-same-origin都会被拒绝function isSupportedSandboxedIframe( frameElement: Element | null, ): frameElement is HTMLIFrameElement { if (!frameElement || frameElement.tagName ! IFRAME) return false; if (!(sandbox in frameElement)) return false; const sandboxTokens Array.from((frameElement as HTMLIFrameElement).sandbox); return sandboxTokens.length 1 sandboxTokens[0] allow-same-origin; }目标断言函数rebuild.ts#L173-L191先检查显式 opt-out然后要求目标文档存在有效的defaultView并且文档已登记在sandboxedRebuildDocuments弱集合中、其frameElement满足上述沙箱条件function assertRebuildTargetAllowed(options: RebuildOptions): void { if (options.UNSAFE_allowUnprotectedRebuild) return; const win options.doc.defaultView; if (!win) return; if ( sandboxedRebuildDocuments.has(options.doc) isSupportedSandboxedIframe(win.frameElement) ) { return; } throw new Error(REBUILD_TARGET_ERROR); }rebuild()主体在解构 options 之前就调用守卫rebuild.ts#L717-L721确保任何节点构建都不会发生在未受保护的目标文档上。需要重点理解的三条边界sandboxedRebuildDocuments弱集合是信任锚点只有通过createSandboxedIframe()创建的 iframe 文档才会被登记rebuild.ts#L784。这意味着即使调用方自己拼一个当前sandboxallow-same-origin的 iframe其文档也不会被信任——沙箱属性必须在 iframe 被附加/使用之前设置并由库自身强制才能防止先建后松的时序漏洞。doc.defaultView为空的场景跳过检查这是为无窗口环境如部分测试环境保留的例外见设计文档的 Error Handling And Compatibility 一节。守卫只属于rebuild()更低层的节点构建函数如buildNodeWithSN()在本次变更中不拥有浏览器沙箱策略的所有权避免把策略散落到每个构建节点。安全辅助 APIcreateSandboxedIframe与rebuildIntoSandboxedIframe为了让直接使用rrweb-snapshot的调用方有一等公民的安全路径包新增了两个导出index.ts#L25-L43。createSandboxedIframe创建并附加一个受保护重建专用的 iframerebuild.ts#L759-L787root必须是**已连接isConnected**到文档的元素否则抛出SANDBOXED_IFRAME_ROOT_ERROR逐项应用调用方传入的iframeAttributes但跳过sandbox属性——调用方无法覆盖或放宽被强制的沙箱策略最后强制iframe.setAttribute(sandbox, allow-same-origin)再附加到root创建后若拿不到contentDocument/contentWindow会回滚移除 iframe 并抛错成功后把文档登记进sandboxedRebuildDocuments。import { createSandboxedIframe } from rrweb-snapshot; const iframe createSandboxedIframe({ root: document.body, iframeAttributes: { title: Replay }, // sandbox 会被忽略并强制覆盖 }); // iframe.getAttribute(sandbox) allow-same-originrebuildIntoSandboxedIframe一步完成建 iframe 重建的首选辅助函数rebuild.ts#L798-L817const { iframe, node } rebuildIntoSandboxedIframe(snapshot, { root: document.body, cache, mirror, });它内部调用createSandboxedIframe()获取受保护 iframe然后以 iframe 文档为doc调用受守卫的rebuild()。返回{ iframe, node }其中node为Node | null与rebuild()的语义一致方便调用方继续调整 iframe 尺寸或管理生命周期。若重建失败新建的 iframe 会被移除后再重抛错误避免在页面上留下半成品。设计文档 2026-05-18-sandboxed-rebuild-design.md 同时强调该辅助函数要求显式root、不默认document.body、总是创建全新 iframe不接受调用方提供的 iframe且首版保持最小化不镜像 replayer 的样式、滚动、指针事件或注入样式行为。Replayer 集成UNSAFE_replayCanvas成为显式不安全路径rrweb重放器在 packages/rrweb/src/replay/index.ts 的setupDom()#L602-L640中保持原有默认 iframe 模型并接入新守卫默认路径调用createSandboxedIframe({ root: this.wrapper })创建重放 iframe获得sandboxallow-same-origin无需不安全 opt-out 即可满足守卫UNSAFE_replayCanvas: true路径手动创建 iframe 并设置sandboxallow-same-origin allow-scripts#L618-L622因为 canvas 重放需要脚本权限此时 iframe 不再满足安全守卫条件。因此重放器在重建首个全量快照时必须把 opt-out 与 canvas 模式绑定#L855rebuild(event.data.node, { doc: this.iframe.contentDocument, afterAppend, cache: this.cache, mirror: this.mirror, UNSAFE_allowUnprotectedRebuild: this.UNSAFE_replayCanvas, });实施计划 2026-05-18-sandboxed-rebuild.md 明确要求不得从其他调用点传递该不安全选项除非有测试证明该调用确实以不受保护的浏览器目标触达守卫路径。这也意味着开启UNSAFE_replayCanvas即主动放弃脚本执行保护属于开发者需要明确承担风险的选择。关于嵌套 iframe 的说明设计文档澄清本次修复不重写嵌套 iframe 的 sandbox 属性。在正常重放中顶层重放 iframe 的沙箱会约束其内部嵌套的浏览上下文因此被序列化的内层 iframe 无法重新启用祖先沙箱已移除的脚本执行能力——这次变更强化的是重建目标文档的边界而非逐一改写每个嵌套 iframe 的沙箱属性。迁移路径与兼容性这是一次对rrweb-snapshot的有意破坏性变更breaking change。对仍在使用旧行为——直接在未受保护的浏览器文档上调用rebuild()——的调用方迁移路径有两条对不可信重放内容改用rebuildIntoSandboxedIframe()或createSandboxedIframe()仅在明确接受脚本执行风险时设置UNSAFE_allowUnprotectedRebuild: truerebuild(node, { doc, cache, mirror, UNSAFE_allowUnprotectedRebuild: true, // 显式承担脚本执行风险 });该选项的价值在于让不安全用法在代码评审中可见同时不阻塞那些有意承担风险的既有测试或工具。官方发布说明changeset将此描述为对rrweb-snapshot的安全加固破坏性变更、对rrweb的 patch 级变更见实施计划 2026-05-18-sandboxed-rebuild.md 的 Task 5。测试验证用浏览器强制执行边界测试用例完整记录了守卫的预期行为见 packages/rrweb-snapshot/test/rebuild.test.tsjsdom 环境与 packages/rrweb/test/replayer.test.tsPuppeteer 驱动对顶层浏览器document调用rebuild()抛出守卫错误对调用方提供的 iframe 文档即使当前恰好是sandboxallow-same-origin同样抛出——因为其文档未被sandboxedRebuildDocuments登记对任何非恰好allow-same-origin的沙箱策略组合、allow-scripts、allow-same-origin allow-scripts、allow-same-origin allow-forms均抛出UNSAFE_allowUnprotectedRebuild: true允许显式承担风险的重建rebuildIntoSandboxedIframe()创建 iframe、重建前应用沙箱并返回重建节点且不允许调用方通过iframeAttributes.sandbox覆盖策略重放器在默认模式与UNSAFE_replayCanvas模式下分别产出allow-same-origin与allow-same-origin allow-scripts的沙箱属性且 canvas 模式在全量快照重建时走显式不安全路径。关键测试命令来自实施计划./node_modules/.bin/vitest run packages/rrweb-snapshot/test/rebuild.test.ts ./node_modules/.bin/cross-env PUPPETEER_HEADLESStrue ./node_modules/.bin/vitest run packages/rrweb/test/replayer.test.ts -t UNSAFE_replayCanvas|supported sandbox policy文档与配套材料该决策同步落地的文档包括docs/sandbox.md明确rebuild()在浏览器环境中强制执行沙箱边界浏览器重建应使用rebuildIntoSandboxedIframe()或createSandboxedIframe()直接对调用方创建的浏览器文档调用rebuild()必须显式 opt-outpackages/rrweb-snapshot/README.md将rebuild描述为低层 API默认要求沙箱化 iframe 目标并给出rebuildIntoSandboxedIframe的推荐用法guide.md更新UNSAFE_replayCanvas选项说明明确开启后重放 iframe 会加入allow-scripts并放弃沙箱脚本执行保护docs/recipes/canvas.md将原有启用 Canvas 重放会移除沙箱的警告升级为对脚本执行保护 opt-out 的完整说明。小结rrweb-snapshot的沙箱化重建决策体现了一条清晰的安全思路与其在净化所有可执行向量的军备竞赛中疲于奔命不如把边界判定交给浏览器 iframe 沙箱。rebuild()默认拒绝不受保护的浏览器目标、createSandboxedIframe()/rebuildIntoSandboxedIframe()提供一等安全路径、UNSAFE_allowUnprotectedRebuild与UNSAFE_replayCanvas让所有不安全用法在代码评审中显式可见——这一组合既堵住了直接调用底层 API 的漏洞又保留了高级用法的逃生舱是安全性与可用性之间的一次务实平衡。赞分享前端可观测性开发工具【免费下载链接】rrwebrecord and replay the web项目地址https://gitcode.com/gh_mirrors/rr/rrweb点击查看免费下载相关推荐gopherlings入门教程从hello world到接口实现的完整学习路径gopherlings入门教程从hello world到接口实现的完整学习路径 gopherlings是一个专为Go语言初学者设计的互动学习项目通过修复一系前端可观测性开发工具rrweb 沙箱化重建Sandboxed Rebuild让 rrweb-snapshot.rebuild() 默认拒绝非受保护浏览器文档rrweb 沙箱化重建Sandboxed Rebuild让 rrweb snapshot.rebuild 默认拒绝非受保护浏览器文档 本文基于仓库内实施计前端可观测性开发工具rrweb 回放沙箱机制解析用 iframe sandbox 隔离脚本行为与重建安全边界rrweb 回放沙箱机制解析用 iframe sandbox 隔离脚本行为与重建安全边界 在 rrweb 的录制 回放体系中回放阶段绝不会重新执行录制页面中前端可观测性开发工具上一篇2025 Codestral优化配置指南让AI模型在本地释放超强代码解释能力下一篇超全UX设计指南程序员必知的交互设计原则与落地实践创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

VuePress 核心包 @vuepress/core Node.js API 深度解析:dev、build 与 eject 源码级实战指南

VuePress 核心包 @vuepress/core Node.js API 深度解析:dev、build 与 eject 源码级实战指南

前端文档SSR 【免费下载链接】vuepress 📝 Minimalistic Vue-powered static site generator 项目地址: https://gitcode.com/gh_mirrors/vu/vuepress 点击查看 免费下载 本篇技术指南聚焦 VuePress 项目中最核心的运行时包 vuepress/core,系…

2026/9/20 5:12:26 阅读更多 →
Claude Code配置指南:API兼容性与VSCode插件调试实战

Claude Code配置指南:API兼容性与VSCode插件调试实战

1. 这不是“又一个AI插件”:Claude Code 的真实定位与能力边界很多人点开这个标题,第一反应是:“哦,又一个让VSCode变聪明的插件”。但如果你真这么想,安装完立刻就会卡在第一步——连基础对话都触发不了,控…

2026/9/21 6:51:09 阅读更多 →
超声波探伤A扫描信号处理与C扫描成像:Python仿真及论文可复现

超声波探伤A扫描信号处理与C扫描成像:Python仿真及论文可复现

简介:这是一份超声波探伤方向的毕业论文文档,面向测控、电子、无损检测等专业的本科或研究生读者,适合作为毕业设计选题参考与课程论文写作范本。论文围绕脉冲反射式超声波检测技术展开,从课题背景与意义入手,先梳理国…

2026/9/21 8:09:31 阅读更多 →

最新新闻

STM32软件SPI驱动1.8寸TFT-LCD完整教程

STM32软件SPI驱动1.8寸TFT-LCD完整教程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/21 10:22:15 阅读更多 →
PCIe 5.0交换芯片如何破解AI集群GPU互联瓶颈

PCIe 5.0交换芯片如何破解AI集群GPU互联瓶颈

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/21 10:22:15 阅读更多 →
2026跨部门协同研发管理系统选型指南:避开踩坑实战解析

2026跨部门协同研发管理系统选型指南:避开踩坑实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/21 10:22:14 阅读更多 →
外贸建站用什么平台好?新手入门避坑指南

外贸建站用什么平台好?新手入门避坑指南

外贸建站用什么平台好?新手入门避坑指南 网站做好了没人访问,这是90%外贸新手最崩溃的时刻。你花了几万块定制开发,页面精美得像杂志,但打开百度或谷歌搜产品,根本找不到你。别慌,这通常不是内容的问题,而是 技术选型 从一开始就错了。…

2026/9/21 9:45:18 阅读更多 →
一个服务器上有两个网站要备案两次吗?源码下载避坑指南

一个服务器上有两个网站要备案两次吗?源码下载避坑指南

一个服务器上有两个网站要备案两次吗?源码下载避坑指南 别再死磕那些丑得令人发指的模板网站了,真的,看着都尴尬。很多新手为了省事,直接去搜“源码下载”,结果装出来的页面配色像上世纪的网吧,布局挤得像早高峰的地铁,客户一眼就能看穿你的不专业。更头疼的是,当你终于搞定两个网站,准备绑上服务器时,卡在了备案…

2026/9/21 9:30:07 阅读更多 →
个人博客网页设计论文选题怎么选,3个维度避开域名服务器坑

个人博客网页设计论文选题怎么选,3个维度避开域名服务器坑

个人博客网页设计论文选题怎么选,3个维度避开域名服务器坑 域名解析报错 502,服务器内存爆满,这种“代码写得好,上线就抓瞎”的尴尬,是不是你写个人博客网页设计论文时的真实写照?很多同学在选题和实操阶段,死磕 CSS 动画或 JS 交互,却对最底层的域名绑定和服务器配置一知半解。…

2026/9/21 9:16:31 阅读更多 →

日新闻

agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程

agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程

agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程 【免费下载链接】agentic-awesome-skills AAS Core is the local, agent-first control plane for complete catalog discovery, agent-owned selection, stack validation, and …

2026/9/21 0:00:01 阅读更多 →
gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析

gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析

gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析 【免费下载链接】gin-vue-admin 🚀ViteVue3Gin拥有AI辅助的基础开发平台,企业级业务AI开发解决方案,内置mcp辅助服务,内置skills管理,…

2026/9/21 0:00:01 阅读更多 →
Wox 全功能插件开发实战指南:基于 Python / Node.js 宿主与 WebSocket 的持久化插件体系

Wox 全功能插件开发实战指南:基于 Python / Node.js 宿主与 WebSocket 的持久化插件体系

桌面应用AI 应用插件系统 【免费下载链接】Wox A cross-platform launcher that simply works 项目地址: https://gitcode.com/gh_mirrors/wo/Wox 点击查看 免费下载 全功能插件(Full-featured Plugin)是 Wox 三类插件实现方式中能力最完整的…

2026/9/21 0:00:01 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/21 3:13:20 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/21 2:19:36 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/21 4:51:05 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/19 23:01:36 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/19 17:50:38 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/19 23:35:34 阅读更多 →