← 返回信息流
AI 资讯Hacker News·1 小时前

托管自执行JavaScript的HTML安全性引发讨论

原标题:Is It Safe to Host HTML That Runs Its Own JavaScript?

速览

该标题提出一个技术安全问题:是否安全地托管一段包含自身JavaScript代码的HTML。这涉及跨站脚本攻击、沙箱隔离、内容安全策略等议题。对于开发者而言,理解此类托管的风险有助于防范潜在漏洞。尽管并非AI相关,但属于前端安全领域的常见话题。

AI 深度解读

背景

2026年7月8日,Hacker News上的一篇技术文章引发讨论:当用户上传的HTML文件中包含任意JavaScript代码时,托管方如何保证安全?ShareMyPage 是一个允许用户分享任意HTML页面的产品,其中大量HTML由AI生成,未经过逐行审查。面对“如何防止注入或会话劫持”的尖锐问题,团队给出了一个反直觉的答案:不清理HTML,而是假设每个页面都是恶意的,并让这个假设变得无关紧要。

核心内容

ShareMyPage 刻意不做任何HTML消毒(sanitize),原因有二:一是消毒会破坏产品核心功能——用户上传的是真正的页面,包含动态图表、可交互原型、小型工具,JavaScript 是灵魂,去掉就只剩截图;二是消毒不信任的HTML是一场永无休止的军备竞赛,防御方只要漏掉一个漏洞就会全线失守。因此团队采取了相反策略:原样运行页面,但把它放在一个无法对你、对我们、对任何打开它的人造成伤害的地方。

整套方案的核心是:不试图让HTML变得安全,而是让运行它的环境变得安全。 文件本身完全不需要被信任。

每个页面运行在无源(origin)的沙箱中

共享页面绝不在主站直接渲染,而是通过一个沙箱化的 iframe 加载。沙箱只授予页面成为“真实页面”所必需的最小权限,而剥夺所有能触及外部的能力。

沙箱中缺少的权限列表比存在的更重要:没有 allow-same-origin。这个单一缺失让被嵌入的文档获得一个不透明的 null origin。其中的脚本仍然可以运行(图表动画、表单提交等),但 document.cookie 返回空,localStorage 不可用,也无法读取或访问嵌入它的父页面。这是一个不属于任何源的活页面。

最后两个条目 allow-popups-to-escape-sandboxallow-top-navigation-by-user-activation 是另一种权限:它们允许用户的点击触达外部世界,比如“预约通话”链接或 mailto 链接可以正常打开,而不是静默失败。这些权限仅限真实用户手势触发,且只控制导航,从不涉及数据——上述关于 cookie、存储和源的保护完全不受影响。

一个永远不打破的规则: allow-scriptsallow-same-origin 绝不能同时出现。一旦组合,被嵌入的脚本就会重新获得真实源,悄悄撤销所有保护。保持这两者分离是承重线。

页面托管在独立的、无 cookie 的源上

沙箱是第一层,第二层是内容从何处提供。HTML 并非来自你登录的应用源,而是从另一个不同的源通过一个短期签名的 URL 提供。该源从不设置或读取 cookie。你的登录会话位于应用源上,在内容源上根本不存在。因此,即使理论上某个脚本突破了沙箱,在那个源上也无物可偷:没有会话 cookie、没有 token、没有任何登录状态。团队代码中的注释写得很直白:即使沙箱逃逸,也找不到任何可窃取的东西。

此外,团队还锁定了只有自己的应用才能嵌入这些内容,防止页面被拖走并用于在其他地方伪装成可信框架。

直接回答注入和会话劫持

注入(通常是跨站脚本 XSS) 是未信任的 HTML 在你信任的、带有 cookie 的源中执行,从而可以读取属于你的 DOM 和 cookie。在 ShareMyPage 上,未信任的 HTML 从未接触那个源——没有可信上下文供它注入。它被架构本身隔离,而不是靠检查。

JavaScript 会话劫持 通过读取会话 cookie 或 token 并重放来实现。在沙箱中,脚本拥有 null origin,所以 cookie 和存储访问在启动前就消失了;而且它运行的源本身就不携带任何会话。两个独立的原因让攻击无处下手。

多层防御,没有单一锁点

好的安全架构不依赖一个巧妙的控制,而是叠加多个独立层,任何单层失效时其余层依然有效:

  • 页面运行在 null-origin 沙箱中,脚本得不到 cookie、存储,也无法触及父页面。
  • 页面从独立的、无 cookie 的源提供,所以那里本来就没有会话可攻击。
  • 页面通过短期签名 URL 加载,不是可猜测的路径,且嵌入仅限自家应用。
  • 你登录的应用本身有严格的内容安全策略,外壳本身无法被嵌入或注入。
  • 每次读取都在服务端鉴权,并限定在你的工作区,因此即使链接泄露也无法打开你无权查看的页面。
  • 页面以私有加密文件形式存储在欧盟,从不使用公开存储桶。

任意一个失效,其余仍起作用。这就是团队所说的“安全是设计本身,而非事后附加的功能”。

不声称的部分

隔离保护你免受页面伤害,也保护我们免受页面伤害,但不把页面作者变成你应信任的人。公开链接依然是公开链接:一个人可以在上面放误导内容,就像在互联网上任何托管平台一样。这是审核问题,而非同源问题。你始终控制最重要的部分:谁可以打开每个页面——从仅自己、到团队、到密码保护、到完全公开。

团队持续加固。目前服务页面上的内容安全策略故意宽松,因为 AI 生成的页面需要合法加载字体和调用 API,而沙箱才是真正承担风险的容器。使上传的 JavaScript 能够安全运行的核心——null-origin 沙箱和独立的无 cookie 源——从第一个页面起就存在,并且不会改变。

关键要点

  • 不消毒 HTML,而是隔离运行环境:放弃传统消毒思路,因为消毒会破坏产品功能且无法彻底防御;改为让运行环境本身安全,文件无需信任。
  • 沙箱剥夺 same-origin 权限:iframe 沙箱中故意不设置 allow-same-origin,使脚本获得 null origin,无法访问 cookie、localStorage,也无法与父页面交互。
  • 独立无 cookie 源:页面内容从另一个不设置 cookie 的源通过短期签名 URL 提供,即使沙箱逃逸也无会话可窃取。
  • 严格禁止 allow-scriptsallow-same-origin 同时出现:这是保护机制的核心承重线,一旦组合,保护会完全失效。
  • 多层防御叠加:沙箱、独立源、签名 URL、应用端 CSP、服务端鉴权、加密存储等多层独立防线,任意一层失效其余仍有效。
  • 不解决内容可信问题:隔离防止恶意脚本危害用户和平台,但不阻止页面作者发布虚假或误导内容——这是审核问题,用户可通过权限控制谁可访问页面。

意义与影响

这篇文章为托管用户生成/上传的任意 HTML/JavaScript 提供了一个全新的安全范式。传统上,行业普遍采用“先消毒后托管”的思路,但实践证明这难以应对不断演变的攻击向量,尤其当 AI 生成的内容大量出现时,逐行审查变得不现实。ShareMyPage 的“隔离而非过滤”设计,将安全焦点从内容本身转移到运行环境,通过 null-origin 沙箱和独立无 cookie 源两个关键措施

查看原文 →sharemypage.app