在计算机安全中,沙箱(Sandbox)是一种用于隔离正在运行程序的安全机制,通常用于执行未经测试或不受信任的程序或代码,它会为待执行的程序创建一个独立的执行环境,内部程序的执行不会影响到外部程序的运行。 其实在前端世界里,沙箱环境无处不在!
iframe 标签可以创造一个独立的浏览器原生级别的运行环境,这个环境由浏览器实现了与主环境的隔离。在 iframe 中运行的脚本程序访问到的全局对象均是当前 iframe 执行上下文提供的,不会影响其父页面的主体功能,因此使用 iframe 来实现一个沙箱是目前最方便、简单、安全的方法。 如果只考虑浏览器环境,可以用 With + Proxy + iframe 构建出一个比较好的沙箱。
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>



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