2026-08-20
JavaScript
0

目录

iframe是天然的优质沙箱

在计算机安全中,沙箱(Sandbox)是一种用于隔离正在运行程序的安全机制,通常用于执行未经测试或不受信任的程序或代码,它会为待执行的程序创建一个独立的执行环境,内部程序的执行不会影响到外部程序的运行。 其实在前端世界里,沙箱环境无处不在!

iframe是天然的优质沙箱

iframe 标签可以创造一个独立的浏览器原生级别的运行环境,这个环境由浏览器实现了与主环境的隔离。在 iframe 中运行的脚本程序访问到的全局对象均是当前 iframe 执行上下文提供的,不会影响其父页面的主体功能,因此使用 iframe 来实现一个沙箱是目前最方便、简单、安全的方法。 如果只考虑浏览器环境,可以用 With + Proxy + iframe 构建出一个比较好的沙箱。

  • 利用 iframe 对全局对象的天然隔离性,将 iframe.contentWindow 取出作为当前沙箱执行的全局对象
  • 将上述沙箱全局对象作为 with 的参数限制内部执行程序的访问,同时使用 Proxy 监听程序内部的访问。
  • 维护一个共享状态列表,列出需要与外部共享的全局状态,在 Proxy 内部实现访问控制。
js
// 初始化沙箱环境 function initSandbox () { // 创建iframe作为隔离环境 const iframe = document.createElement('iframe'); iframe.style.display = 'none'; iframe.sandbox = 'allow-scripts allow-same-origin'; document.body.appendChild(iframe); // 等待iframe加载完成 return new Promise((resolve) => { iframe.onload = () => { const iframeWindow = iframe.contentWindow; const iframeDocument = iframe.contentDocument; // 允许访问的全局变量白名单 const ALLOWED_GLOBALS = [ 'console', 'setTimeout', 'clearTimeout', 'setInterval', 'clearInterval', 'Date', 'Array', 'Object', 'String', 'Number', 'Math', 'JSON', 'isNaN', 'parseInt', 'parseFloat', 'document', 'window' ]; // 受保护的全局变量(禁止修改) const PROTECTED_GLOBALS = [ 'window', 'document', 'console', 'location', 'history', 'localStorage', 'sessionStorage' ]; // 创建代理全局对象 const proxyGlobal = new Proxy(iframeWindow, { get (target, key) { // 白名单检查 if (ALLOWED_GLOBALS.includes(key)) { return target[key]; } // 特殊处理document if (key === 'document') { return iframeDocument; } // 禁止访问的全局变量 console.warn(`[沙箱安全拦截] 禁止访问: ${String(key)}`); return undefined; }, set (target, key, value) { // 防止修改重要全局对象 if (PROTECTED_GLOBALS.includes(key)) { console.error(`[沙箱安全拦截] 禁止修改: ${key}`); return true; } // 允许在沙箱内定义全局变量 target[key] = value; return true; }, has () { // 始终返回true,保证with语句工作正常 return true; } }); // 创建沙箱执行函数 const executor = function (code) { try { // 使用with语句将代码作用域绑定到代理对象 const wrappedCode = `with (sandboxProxy) { ${code} }`; // 在iframe中执行代码 new iframeWindow.Function('sandboxProxy', wrappedCode)(proxyGlobal); } catch (e) { console.error('沙箱执行错误:', e); } }; resolve({ executor: executor, iframe: iframe }); }; iframe.src = 'about:blank'; }); }
html
<script> // 沙箱执行器实例 let sandboxExecutor = null; // 执行用户代码 async function executeInSandbox () { if (!sandboxExecutor) { const result = await initSandbox(); sandboxExecutor = result.executor; } window.abc = 123; const code = ` console.log(window) console.log(history == window.history); window.abc = 'sandbox'; console.log('沙箱内: ',window.abc); ` const result = sandboxExecutor(code) console.log(result) console.log("沙箱外: ",window.abc) } // 页面加载完成后初始化沙箱环境 window.addEventListener('DOMContentLoaded', () => { // 初始化沙箱(预加载) initSandbox().then(result => { sandboxExecutor = result.executor; console.log('沙箱环境初始化完成'); }); }); </script>

image.png

如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:繁星

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!