JavaScriptWeb APISecurity

ShadowRealm - JavaScript 代码隔离的新方案

Mat Marquis··原文链接
收录于 2026/5/27 23:39:53

JavaScript 的 Realm 概念

单线程的准确说法:

  • ❌ "JavaScript 是单线程语言"
  • ✅ "一个 JavaScript realm 是单线程的"

什么是 Realm?

  • 浏览器标签页 = 一个 realm(主线程)
  • Web Worker = 一个 realm(worker 线程)
  • 跨域 <iframe> = 独立 realm(自己的主线程)

关键特性:

  • 每个 realm 有自己的全局对象(WindowglobalThis
  • 每个 realm 有自己的内置对象(ArrayObject 等)
  • 不同 realm 的全局对象不共享不干扰
// 外部页面和 iframe 是两个独立的 realm
window.globalThis === iframe.contentWindow.globalThis
// Result: false

window.Array === iframe.contentWindow.Array
// Result: false

全局作用域污染问题

历史遗留问题:

  • 函数声明会污染全局对象
  • 第三方库、polyfill、广告、分析脚本都在污染全局环境
  • 潜在冲突:框架 A 改了 Array.prototype,库 B 崩了

理想方案:

  • 一个"干净房间"执行隔离代码
  • 不影响主 realm,也不受主 realm 影响
  • 但不能用 Web Worker — 因为跨线程通信受限,无法在主线程上执行

ShadowRealm API

核心设计:

  • 新的 realm 类型,专门用于隔离
  • 没有自己的执行线程 — 代码仍在主线程执行
  • 有自己的全局对象和内置对象
  • 与主 realm 完全隔离

API 只有 2 个方法:

1. evaluate() — 执行字符串代码

const shadow = new ShadowRealm();

// 主 realm 有全局函数
function globalFunction() {}
console.log(globalThis.globalFunction); // function globalFunction()

// ShadowRealm 里没有
console.log(shadow.evaluate('globalThis.globalFunction')); // undefined
// 在 ShadowRealm 里声明函数
shadow.evaluate('function secret() { return "hidden"; }');

// 主 realm 访问不到
console.log(globalThis.secret); // undefined

// 但可以通过 shadow 访问
console.log(shadow.evaluate('globalThis.secret')); // function secret()

2. importValue() — 动态导入模块

// spookycode.js
export function greeting() {
  return "Hello from the ShadowRealm!";
}

// 主程序 {
  const shadow = new ShadowRealm();
  
  // 导入模块并获取导出值
  const shadowGreet = await shadow.importValue("./spookycode.js", "greeting");
  
  console.log(shadowGreet()); // "Hello from the ShadowRealm!"
}

安全边界说明

不是真正的安全边界:

  • ShadowRealm 代码仍可推断其他 realm 的信息
  • 提供完整性边界,而非安全边界

什么是完整性边界:

  • 代码不能直接干扰其他 realm 的对象
  • 除非你主动允许(通过函数绑定)
const shadow = new ShadowRealm();

// 返回一个函数,可以修改 ShadowRealm 内的全局变量
const setShadowValue = shadow.evaluate('(v) => globalThis.x = v');

setShadowValue("secret");

// 主 realm 不受影响
console.log(globalThis.x); // undefined

// 但可以从 ShadowRealm 读取
console.log(shadow.evaluate('globalThis.x')); // "secret"

当前进度

状态说明
Stage 2.7已批准原则,正在验证
Stage 3浏览器开始实现(未来)
Stage 4纳入 ECMAScript 标准

注意: 目前还是提案,浏览器暂未实现。

使用场景

  • 在"干净房间"运行测试套件,避免 mock 数据污染
  • 隔离第三方库,防止全局污染
  • 执行不信任代码(如插件系统)
  • 无限多个一次性沙箱环境

核心结论

ShadowRealm 提供轻量级代码隔离,在主线程执行但不受全局污染影响。API 简洁(仅 evaluate + importValue),适合测试、插件、第三方库隔离等场景。目前已到 Stage 2.7,离浏览器实现不远了。