深入理解 JavaScript 沙箱

在 JavaScript 中,沙箱是一种安全机制,用于隔离运行代码。

这种隔离确保了一个标签页中的 JavaScript 代码无法访问另一个标签页的内容,除非这两个页面遵循同源策略,或者通过 CORS 明确允许跨域访问。Web 代码必须通过 IPC 与浏览器内核进程通信。沙箱设计的目的是允许不信任的代码在特定环境中运行,限制其对隔离区域外资源的访问。

沙箱的使用场景是什么?

沙箱通常用于隔离代码的执行环境,以确保其在安全的环境中运行。

  • 执行第三方库:当你需要运行不可信的第三方 JavaScript 代码时。
  • 在线代码编辑器:许多在线代码编辑器在沙箱中执行用户提供的代码。
  • Web 应用程序安全:在浏览器中运行来自不同来源的 JavaScript 时,沙箱可以限制其权限,防止恶意代码访问敏感资源。
  • 插件和第三方脚本:当 Web 应用程序需要加载和执行第三方插件或脚本时,沙箱可以限制它们的访问权限。
  • JSONP:当解析来自服务器的 JSONP 响应时,如果返回的数据不可信,沙箱可以安全地解析数据。

JSONP 通信机制

JSONP 的工作原理基于 <script> 标签没有跨域限制(这是一个历史遗留问题),允许与第三方服务进行通信。如下所示:

html 复制代码
<script src="http://www.example.net/api?param1=1&param2=2"></script>

使用沙箱解析 JSONP 数据的基本方法

  1. 创建一个 iframe 作为沙箱:在主页面中动态创建一个 iframe,为 JSONP 请求提供一个执行环境。这个 iframe 无法访问主页面的 DOM。
  2. 在 iframe 内发起 JSONP 请求 :将一个 <script> 标签插入到创建的 iframe 中,使返回的 JSONP 脚本在隔离环境中执行。
  3. 安全地从 iframe 中检索数据 :iframe 内的脚本无法直接修改主页面的 DOM,但可以使用预定义的方法(如 postMessage API)安全地将数据传输到主页面。
  4. 限制和监控 iframe 的行为 :额外的安全措施包括使用 内容安全策略(CSP) 限制 iframe 可以加载的资源。

通过遵循这些步骤,即使 JSONP 包含不可信的数据,也可以有效隔离,从而保护用户数据和安全。

使用 with + new Function 实现沙箱

在 JavaScript 中,你可以使用 with 语句和 new Function 创建一个简单的沙箱环境。

with 块的作用域内,变量访问将优先考虑对象的属性,然后再沿着作用域链向上查找。有效地监控代码中的变量访问:

js 复制代码
function createSandbox(code) {
    // 创建一个空对象,作为沙箱内的全局对象
    const sandbox = {};
    // 使用 with 语句将代码的作用域设置为这个空对象
    // 使用 new Function 创建一个新函数,限制代码只能访问沙箱对象
    const script = new Function('sandbox', `with(sandbox) { ${code} }`);
    // 执行函数,并将沙箱对象作为参数传递
    return function () {
        script(sandbox);
    };
}

// 使用沙箱环境
const sandboxedScript = createSandbox('console.log("Hello from the sandbox!"); var x = 10;');
sandboxedScript(); // 输出:Hello from the sandbox!
console.log(typeof x); // 输出:undefined,因为 x 是在沙箱内定义的,无法在外部访问

在这里,我们定义了一个 createSandbox ,它接受一个字符串。这个字符串代表需要在沙箱环境中执行的代码。

首先创建一个空对象 sandbox,它作为沙箱内的全局对象。然后,我们使用 with(sandbox) 语句将沙箱代码的执行环境设置为这个空对象,这意味着所有变量和函数定义都将被限制在沙箱内,无法访问全局作用域。

new Function 构造函数创建了一个新函数,用于执行代码字符串。最后,我们返回一个闭包函数,当调用时,执行沙箱代码。

限制和安全问题

尽管这种方法可以在一定程度上隔离执行环境,但它并不完全安全。with 语句和 new Function 存在安全风险。如果沙箱代码可以访问 Function 构造函数本身,这可能会使其绕过沙箱限制并执行任意代码。

使用 iframe 实现沙箱

使用 iframe 创建沙箱环境是 Web 开发中的一种常见技术。它允许在当前页面内嵌入一个完全独立的 HTML 页面,防止脚本访问主页面的 DOM ,从而增强安全性。

HTML 结构

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>沙箱示例</title>
</head>
<body>
    <iframe id="sandbox" style="display: none"></iframe>
    <script src="index.js"></script>
</body>
</html>

JavaScript 实现

index.js 中,我们可以通过操作 iframecontentWindow 属性来创建一个简单的沙箱环境。这里的思路是在 iframe 内注入和执行脚本,确保在隔离的环境中运行:

js 复制代码
// index.js
function createSandbox(callback) {
    const iframe = document.getElementById('sandbox');
    if (!iframe) {
        return console.error('未找到沙箱 iframe');
    }

    // 确保 iframe 完全加载后再执行代码
    iframe.onload = function () {
        const iframeWindow = iframe.contentWindow;

        // 如有需要,在沙箱内定义一些安全的全局变量或函数
        iframeWindow.safeGlobalVar = {
            /* 安全数据或方法 */
        };

        // 执行回调函数,将沙箱的 window 对象传递进去以在其中运行代码
        callback(iframeWindow);
    };

    // 重新加载 iframe 以确保环境干净
    iframe.src = 'about:blank';
}

// 使用沙箱
createSandbox(function (sandboxWindow) {
    // 在沙箱内执行代码
    sandboxWindow.eval('console.log("Hello from the sandbox!");');
});

iframe 沙箱的限制

使用 iframe 进行沙箱有一些内置的限制:

  • 沙箱内的 <script> 标签无法执行。
  • 不允许进行 AJAX 请求。
  • 无法访问本地存储(例如 localStoragecookie)。
  • 无法创建新弹窗(如通过 window.open 创建)。
  • 无法提交表单。
  • 无法加载插件(如 Flash)。

然而,HTML5 引入了 sandbox 属性 ,它提供了额外的限制,以进一步增强安全性。sandbox 属性支持以下值:

  • allow-scripts:允许执行脚本。
  • allow-same-origin:允许与同源的文档进行交互。
  • allow-forms:允许表单提交。
  • allow-popups:允许弹窗,例如通过 window.open 创建的弹窗。
  • allow-top-navigation:允许导航到顶级框架。

以下是一个使用 sandbox 属性的 iframe 示例:

html 复制代码
<iframe src="sandbox.html" sandbox="allow-scripts" id="sandbox"></iframe>

主页面与 iframe 之间的安全通信

我们可以使用 postMessage 在主页面和沙箱 iframe 之间安全地交换数据。

首先,在主页面中:

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <title>主页面</title>
</head>
<body>
    <iframe src="./sandbox.html" id="sandbox" style="width: 600px; height: 400px"></iframe>

    <script>
        var iframe = document.getElementById('sandbox');

        // 等待 iframe 加载完成
        iframe.onload = function () {
            var targetOrigin = 'http://127.0.0.1:5500/'; // 替换为 iframe 的实际来源
            iframe.contentWindow.postMessage('Hello, sandbox!', targetOrigin);
        };

        // 监听来自 iframe 的消息
        window.addEventListener('message', function (event) {
            // 验证消息来源
            if (event.origin !== 'http://127.0.0.1:5500') {
                return; // 忽略来自不受信任来源的消息
            }

            // 处理接收到的消息
            console.log('从 iframe 收到消息:', event.data);
        });
    </script>
</body>
</html>

iframe 页面(sandbox.html)中,我们监听消息:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>沙箱</title>
</head>
<body>
    <script>
        window.addEventListener('message', function (event) {
            // 验证消息来源
            if (event.origin !== 'http://127.0.0.1:5500') {
                return; // 忽略来自不受信任来源的消息
            }

            // 处理接收到的消息
            console.log('收到消息:', event.data);

            // 向主页面发送响应
            event.source.postMessage('Hello, main page!', event.origin);
        });
    </script>
</body>
</html>

通过验证 event.origin 并在 postMessage 中指定确切的目标来源,我们可以确保安全通信。

使用 Web Workers 实现沙箱

使用 Web Workers 作为沙箱涉及动态创建一个 Blob 对象。这种方法允许在单独的线程中执行任意 JavaScript 代码,同时确保代码与主页面的环境隔离。提供了一种安全执行代码的方式。

js 复制代码
function workerSandbox(appCode) {
    var blob = new Blob([appCode]);
    var appWorker = new Worker(window.URL.createObjectURL(blob));
}

workerSandbox('const a = 1; console.log(a);'); // 输出:1

console.log(a); // ReferenceError: a 未定义

这种方法利用 Web Workers 在隔离环境中执行不可信代码,确保其不会干扰主页面。Web Workers 特别适用于处理计算密集型任务,因为它们可以保持 UI 响应,同时运行独立脚本。

总结

JavaScript 沙箱 是一个执行环境,允许代码在其中运行,而不会影响主应用程序的状态。通过 限制对全局变量和函数的访问,沙箱提供了一种安全执行不可信代码的方式,同时防止潜在的安全风险和数据泄露。

实现沙箱的不同方法包括:

  1. with + new Function
    • 提供基本隔离。
    • 并不完全安全,因为沙箱代码可能通过 Function 构造函数逃脱。
  1. iframe 沙箱
    • 使用嵌入的 iframe 创建一个独立的执行环境。
    • 增强安全性,但存在一些限制(例如,限制 localStorageAJAX 请求)。
    • 可以与 postMessage 结合使用以实现安全的数据交换。
  1. Web Workers
    • 在单独的线程中执行代码。
    • 提供强大的隔离,但无法直接访问 DOM。
    • 理想情况下用于运行计算密集型脚本。

通过选择合适的沙箱技术,开发人员可以提高安全性确保脚本的安全执行,同时将对主应用程序的风险降至最低。

原文:dev.to/leapcell/a-...

相关推荐
qianmoQ28 分钟前
第五章:工程化实践 - 第三节 - Tailwind CSS 大型项目最佳实践
前端·css
椰果uu43 分钟前
前端八股万文总结——JS+ES6
前端·javascript·es6
微wx笑1 小时前
chrome扩展程序如何实现国际化
前端·chrome
~废弃回忆 �༄1 小时前
CSS中伪类选择器
前端·javascript·css·css中伪类选择器
CUIYD_19891 小时前
Chrome 浏览器(版本号49之后)‌解决跨域问题
前端·chrome
IT、木易1 小时前
跟着AI学vue第五章
前端·javascript·vue.js
薛定谔的猫-菜鸟程序员1 小时前
Vue 2全屏滚动动画实战:结合fullpage-vue与animate.css打造炫酷H5页面
前端·css·vue.js
春天姐姐2 小时前
vue3项目开发总结
前端·vue.js·git
谢尔登2 小时前
【React】React 性能优化
前端·react.js·性能优化
来一碗刘肉面2 小时前
TypeScript - 属性修饰符
前端·javascript·typescript