HeatMapXHeatMapX
价格登录

安全预览外部网站——代理 + 净化 + iframe sandbox 的实现

HeatMapX Engineering Team5 min read
  • engineering
  • security
  • nextjs
  • iframe

本文要点

  • 将外部网站加载到自有域名的 iframe 中时,外部 JS 有可能触碰到自身会话,存在安全隐患
  • 应对方法是“带 SSRF 防护的代理 + 移除外部 JS + 限制性 CSP + iframe sandbox”的多层防御
  • sandbox 设置过严会连自家脚本也一并挡住,忘记添加 allow-scripts 就是一个典型陷阱

HeatMapX 的 A/B 测试中有一个功能:将“修改后(Variant B)的效果”应用到目标页面上进行预览。从技术角度看,这实际上是“在自有域名的 iframe 内安全地展示用户的外部网站”,处理起来相当微妙。下面分享一下设计思路,以及实际踩过的坑。

为什么简单实现是危险的

如果直接获取外部网站的 HTML 并在自有域名(例如 heatmapx.com)下原样提供,那么该 HTML 中包含的 <script>onclick= 等代码就会 以自有域名的权限被执行。这样一来,Cookie 和登录会话就有被读取的风险。这是必须避免的情况。

多层防御的构成

因此,我们叠加了以下几层防护。

1. 带 SSRF 防护的代理

对目标 URL 进行校验,拒绝 http/https 以外的协议,并进一步屏蔽 localhost、私有 IP 以及云平台的元数据接口(如 169.254.169.254 等)。同时不自动跟随重定向,而是通过 redirect: 'manual' 逐段重新校验,防止被引导至内部网络。

2. 移除外部 JS(净化)

从获取到的 HTML 中移除 <script> 标签、on*= 内联事件处理器以及 javascript: 协议。因为预览并不需要执行客户网站的 JS(无法完全还原 SPA 是我们已知并接受的取舍)。

3. 限制性 CSP 与 base href

<head> 之后插入 <base href>,用于解析相对路径的图片和 CSS,同时在响应中附加“不执行外部 JS”的限制性 CSP。图片和 CSS 出于展示需要予以放行。

4. iframe sandbox

最后,为展示用的 iframe 添加 sandbox 属性进行隔离。

踩过的坑:sandbox 设置过严

预览功能的原理是:向获取到的 HTML 中注入一段“应用变更集(changeSet)”的自有小脚本,并在 iframe 内执行以改变页面效果。然而,预览用 iframe 的 sandbox 属性只设置了 allow-same-origin却没有添加 allow-scripts

结果就是自家脚本也无法执行,导致出现 变更未生效、仍显示原始页面 的问题。而用于编辑的编辑器侧 iframe 使用的是 allow-scripts allow-same-origin,运行正常,可见问题根源在于两者配置不一致。

修复方式很简单:让预览侧的配置也统一为 allow-scripts allow-same-origin。同时,我们新增了一项回归测试,用于校验 iframe 的 sandbox 属性。

sandbox="allow-same-origin"  →  sandbox="allow-scripts allow-same-origin"

经验总结

  • 嵌入外部内容时要以“多层防御”的思路考虑,不要依赖单一防护手段。
  • sandbox 并非“加上就安全”,关键在于 精准地只放行必要的权限。设置过严会连自身功能都一并挡住。
  • 对于“执行相同处理的两条路径”(编辑器与预览),务必确认它们的安全属性是否一致。只修好其中一处,另一处就可能出问题。

总结

外部网站的安全预览,可以通过 SSRF 防护、移除外部 JS、限制性 CSP、iframe sandbox 这一多层防御体系来实现。而 sandbox 的权限设置,无论过少还是过多都会带来问题。这次忘记添加 allow-scripts 正是一个典型案例。

从 Claude Code 运行的热力图,免费开始。

粘贴一行追踪标签,从 CLI 获取分析和改进建议。