腾讯一面:你了解js的沙箱环境吗?

news2025/1/18 4:35:28

去年的面试了,最近复盘了一下,发现菜的一批,有些问题一下子就答出来了,现在答的话,那时候还在瞎鸡儿答我也不知道答的什么。。。。

在 JavaScript 中,沙箱(sandbox)是一个安全机制,用于隔离运行代码,以防止代码对其它部分的应用程序或系统造成不必要的影响或安全风险。沙箱提供了一个受控环境,在这个环境中,代码可以被执行而不影响外部环境,从而保护用户数据和系统安全。

在浏览器中,沙箱通常指的是浏览器为每个标签页提供的隔离环境。这种隔离保证了一个标签页中的 JavaScript 代码无法访问另一个标签页中的内容,除非两个页面遵循同源策略(即协议、域名和端口号都相同)或者通过 CORS(跨源资源共享)明确允许了跨源访问。网页 web 代码内容必须通过 IPC 通道才能与浏览器内核进程通信,通信过程会进行安全的检查。沙箱设计的目的是为了让不可信的代码运行在一定的环境中,从而限制这些代码访问隔离区之外的资源。

js 中沙箱的使用场景有什么

在 JavaScript 中,沙箱通常用于隔离和控制代码执行的环境,以确保代码运行在一个安全的、受限制的环境中,避免对主环境造成潜在的损害。

  1. 执行第三方 js:当你有必要执行第三方 js 的时候,而这份 js 文件又不一定可信的时候;

  2. 在线代码编辑器:相信大家都有使用过一些在线代码编辑器,而这些代码的执行,基本都会放置在沙箱中,防止对页面本身造成影响;

  3. Web 应用安全: 在浏览器中运行来自不同来源的 JavaScript 代码时,沙箱可以限制这些代码的权限,防止恶意代码访问敏感资源或执行危险操作。

  4. 插件和第三方脚本: 当 Web 应用需要加载和执行第三方插件或脚本时,通过沙箱可以限制这些脚本的访问权限,保护主应用的安全和数据。

  5. jsonp:解析服务器所返回的 jsonp 请求时,如果不信任 jsonp 中的数据,可以通过创建沙箱的方式来解析获取数据;

jsonp 通信机制

JSONP 的工作原理就是通过利用 <script> 标签没有跨域限制的“漏洞”(历史遗迹啊)来达到与第三方通讯的目的。当需要通讯时,本站脚本创建一个 <script> 元素,地址指向第三方的 API 网址,形如:

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

并提供一个回调函数来接收数据(函数名可约定,或通过地址参数传递)。 第三方产生的响应为 json 数据的包装(故称之为 jsonp,即 json padding),形如: callback({"name":"hax","gender":"Male"}) 这样浏览器会调用 callback 函数,并传递解析后 json 对象作为参数。本站脚本可在 callback 函数里处理所传入的数据。

下面是如何通过创建沙箱环境来解析 JSONP 数据的基本思路:

  1. 创建独立的 iframe 作为沙箱:通过在主页面中动态创建一个 iframe,可以为 JSONP 请求提供一个隔离的执行环境。这个 iframe 没有访问主页面 DOM 的权限,从而限制了其中执行的代码对主页面可能造成的影响。

  2. 在 iframe 中发起 JSONP 请求:将 JSONP 请求的 <script> 标签插入到上述创建的 iframe 中,这样由 JSONP 请求返回的脚本就会在这个隔离的环境中执行。这意味着,即使返回的脚本中包含恶意代码,其影响也被限制在了 iframe 中,不会波及到主页面。

  3. 安全地从 iframe 中获取数据:虽然 iframe 中的脚本不能直接修改主页面的 DOM,但它仍然可以通过预定的方式(如通过 postMessage API)安全地将数据传递给主页面。主页面需要设置事件监听器来接收这些数据,并确保通过验证数据来源等措施来保证数据的安全性。

  4. 限制和监控 iframe 中的行为:进一步增强安全性的措施包括使用 Content Security Policy (CSP)来限制 iframe 中可以加载和执行的资源,以及使用其他浏览器安全特性来监控和控制 iframe 中的行为。

通过以上步骤,即使 JSONP 响应中包含不可信的数据或代码,其潜在的危害也可以被有效地隔离和控制,从而保护了用户的数据和安全。

总而言之,当你要解析或执行不可信的 JS 的时候,当你要隔离被执行代码的执行环境的时候,当你要对执行代码中可访问对象进行限制的时候,沙箱就派上用场了。

with + new Function 实现沙箱

在 JavaScript 中,使用 with 语句和 new Function 可以创建一个简单的沙箱环境。这种方式可以限制代码运行时的作用域,防止它访问全局变量或执行不安全的操作。

在 with 的块级作用域下,变量访问会优先查找你传入的参数对象,之后再往上找,所以相当于你变相监控到了代码中的变量访问:

function createSandbox(code) {
  // 创建一个空对象,用作沙箱环境中的全局对象
  const sandbox = {};
  // 使用with语句将代码的作用域设置为这个空对象
  // 使用new Function创建一个新的函数,限制代码访问外部作用域,只能访问sandbox内的变量和函数
  const script = new Function("sandbox", `with(sandbox) { ${code} }`);
  // 执行这个函数,并传入sandbox作为参数
  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 函数,它接受一个字符串 code 作为参数,这个字符串是要在沙箱环境中执行的代码。我们首先创建一个空对象 sandbox,这个对象将作为沙箱的全局对象。然后,我们使用 with(sandbox)语句将沙箱中的代码执行环境设置为这个空对象,这意味着代码中所有的变量和函数定义都将局限在这个空对象内,无法访问外部的全局对象和变量。

new Function 构造函数创建一个新的函数,它执行传入的代码字符串。通过这种方式,我们能够动态执行代码,同时限制这段代码的作用域,防止它访问或修改沙箱外部的环境。最后,我们返回一个闭包函数,这个函数在被调用时会执行沙箱中的代码。

需要注意的是,虽然这种方法可以在一定程度上隔离执行环境,但它并不是完全安全的。with 和 new Function 都有一些安全隐患,特别是如果沙箱中执行的代码可以访问到 Function 构造器本身,它就可能绕过沙箱限制,执行任意代码。可见,new Function + with 的这种沙箱方式,防君子不防小人。

iframe 实现沙箱

使用 iframe 创建沙箱环境是 Web 开发中常见的一种技术,它允许你在当前页面内嵌套一个完全独立的 HTML 页面。这种方法可以有效隔离 JavaScript 执行环境,防止脚本访问主页面的 DOM 或 JavaScript 环境,从而提高安全性。以下是如何使用 iframe 来实现沙箱环境的一个详细代码示例:

首先我们需要编写 HTML 结构:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <iframe id="sandbox" style="display: none"></iframe>

    <script src="index.js"></script>
  </body>
</html>

我们再在 index.js,在 js 代码中,通过操作这个 iframe 的 contentWindow 属性来实现一个简单的沙箱环境。这里的思路是向 iframe 注入脚本并执行,这样执行的脚本就在 iframe 的独立环境中运行,与主页面隔离:

// 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!");');
});

在上面的这些代码中,createSandbox 函数接收一个回调函数作为参数,该回调函数会在 iframe 加载完成后执行。在回调函数中,我们可以通过 iframe 的 contentWindow 属性来访问并操作 iframe 内的全局对象,包括定义全局变量、函数或者通过 eval 执行一些 JavaScript 代码。

但是这些也会带来一下限制:

  1. script 脚本不能执行

  2. 不能发送 ajax 请求

  3. 不能使用本地存储,即 localStorage,cookie 等

  4. 不能创建新的弹窗和 window

  5. 不能发送表单

  6. 不能加载额外插件比如 flash 等

但是别担心,为了进一步提高安全性,HTML5 引入了 sandbox 属性,它可以限制 iframe 中代码的能力。sandbox 属性可以采用以下值:

  1. allow-scripts: 允许执行脚本。

  2. allow-same-origin: 允许与包含文档同源的文档交互。

  3. allow-forms: 允许表单提交。

  4. allow-popups: 允许弹窗,比如通过 window.open 方法。

  5. allow-top-navigation: 允许通过链接导航到顶级框架。

使用 sandbox 属性的 iframe 示例:

<iframe src="sandbox.html" sandbox="allow-scripts" id="sandbox"></iframe>

接下来我们结合 postMessage API,将你需要执行的代码,和需要暴露的数据传递过去,然后和我们的 iframe 页面通信就行了。

首先,在主页面中,我们需要获取 iframe 的引用并使用 postMessage 方法发送消息。消息可以是任何可序列化的对象。

<!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 () {
        // 向iframe发送消息
        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("Received message from iframe:", event.data);
      });
    </script>
  </body>
</html>

postMessage 的第一个参数是要发送的消息,第二个参数是接收消息页面的源(origin),这是一个安全特性,以确保消息只被发送到指定的来源页面。

在 iframe 页面中,我们需要监听 message 事件来接收主页面发送过来的信息:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      window.addEventListener("message", function (event) {
        // 检查消息来源是否符合预期

        if (event.origin !== "http://127.0.0.1:5500") {
          return; // 来源不匹配时忽略消息
        }

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

        // 回复消息给主页面
        event.source.postMessage("Hello, main page!", event.origin);
      });
    </script>
  </body>
</html>

在使用 postMessage 进行通信时,始终要检查 event.origin,并且在发送消息时指定精确的目标 origin,这样可以防止潜在的安全问题。通过这种方式,即使在跨域的情况下,主页面与 iframe 或新窗口之间也能安全、有效地进行数据交换。

20240315220045

Web Workers 实现沙箱

使用 Web Workers 作为沙箱的方法,通过动态创建一个 Blob 对象来包含你想在 Worker 中执行的 JavaScript 代码,然后使用这个 Blob 对象创建一个 Worker。这种方式的好处是它允许你动态地执行任意的 JavaScript 代码,同时确保这些代码在一个与主页面环境隔离的 Worker 中运行,提供了一种隔离执行代码的手段。

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); // a not defined

这种使用 Web Workers 实现沙箱的方法为在 Web 应用中隔离和执行 JavaScript 代码提供了一种有力的手段,特别适合那些需要保持 UI 响应性而又要执行复杂或潜在风险代码的场景。

总结

JavaScript 的沙箱环境是一种隔离执行环境,允许运行和测试代码而不干扰主应用程序的状态或数据。它通过限制代码访问全局变量和函数,提供了一个安全的方式来执行不信任的代码,避免潜在的安全风险和数据泄露。沙箱环境对于保护应用程序免受恶意代码或不确定性代码影响至关重要。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1608458.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

目标检测——大规模鱼类数据集

一、重要性及意义 生物多样性研究&#xff1a;鱼类是水生生态系统中的重要组成部分&#xff0c;其种类多样性对于维持生态平衡至关重要。通过对鱼类进行准确的分割和分类&#xff0c;可以更好地了解不同鱼类的生态习性、分布情况以及与其他生物的相互作用&#xff0c;进而为保护…

单片机基础知识 07

一. 键盘检测 键盘分为编码键盘和非编码键盘。 编码键盘 &#xff1a;键盘上闭合键的识别由专用的硬件编码器实现&#xff0c;并产生键编码号或者键值&#xff0c;如计算机键盘。 非编码键盘&#xff1a;靠软件编程来识别。 在单片机组成的各种系统中&#xff0c;用的较多的…

Echarts-知识图谱

Echarts-知识图谱 demo地址 打开CodePen 效果 思路 1. 生成根节点 2. 根据子节点距离与根节点的角度关系&#xff0c;生成子节点坐标&#xff0c;进而生成子节点 3. 从子节点上按角度生成对应的子节点 4. 递归将根节点与每一层级子节点连线核心代码 定义节点配置 functio…

将 Notepad++ 添加到右键菜单

目录 方式一&#xff1a;添加注册表&#xff08;手动&#xff09; 方式二&#xff1a;添加注册表&#xff08;一键添加&#xff09; 有时安装了notepad后&#xff0c;在txt文件上右键&#xff0c;在弹出的菜单栏中没有【通过 Notepad 打开】&#xff0c;如下&#xff1a; 这…

5. Django 探究CBV视图

5. 探究CBV视图 Web开发是一项无聊而且单调的工作, 特别是在视图功能编写方面更为显著. 为了减少这种痛苦, Django植入了视图类这一功能, 该功能封装了视图开发常用的代码, 无须编写大量代码即可快速完成数据视图的开发, 这种以类的形式实现响应与请求处理称为CBV(Class Base…

OSPF综合大实验

1、R4为ISP&#xff0c;其上只配置IP地址&#xff1b;R4与其他所直连设备间均使用公有IP&#xff1b; 2、R3-R5、R6、R7为MGRE环境&#xff0c;R3为中心站点&#xff1b; 3、整个OSPF环境IP基于172.16.0.0/16划分&#xff1b;除了R12有两个环回&#xff0c;其他路由器均有一个环…

Reka Core, Flash, and Edge: A Series of Powerful Multimodal Language Models

Reka Core, Flash, and Edge: A Series of Powerful Multimodal Language Models 相关链接&#xff1a;arxiv 关键字&#xff1a;Multimodal Language Models、Reka Core、Reka Flash、Reka Edge、State-of-the-Art 摘要 我们介绍了 Reka Core、Flash 和 Edge&#xff0c;这是…

VOJ islands打炉石传说 题解 二进制枚举

islands打炉石传说 代码 #include <bits/stdc.h> using namespace std; typedef long long ll; struct node {int cost, d, w; }; int main() {ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int n; // n张牌cin >> n;vector<node> v(n);for (int i 0; …

【深度学习实战(9)】三种保存和加载模型的方式

一、state_dict方式&#xff08;推荐&#xff09; torch.save(model.state_dict(), PATH)model YourModel() model.load_state_dict(torch.load(PATH)) model.eval()记住一定要使用model.eval()来固定dropout和归一化层&#xff0c;否则每次推理会生成不同的结果。 二、整个…

校园小情书微信小程序源码/社区小程序前后端开源/校园表白墙交友小程序

校园小情书前端代码&#xff0c;好玩的表白墙、树洞、校园论坛&#xff0c;可独立部署&#xff0c;也可以使用我部署的后台服务&#xff0c;毕业设计的好项目。 搭建教程&#xff1a; 一、注册管理后台 1、登录小情书站点进行注册&#xff1a;https://你的域名 2、注册成功…

Mathtype用法记录

常用写法 公式编号 给公式插入编号的方法 手动修改公式编号为指定值 例如编号(8.3.1)修改为(8.3.7)&#xff0c;即章、节号不变&#xff0c;公式序号改为7。 可修改编号的域代码&#xff0c;比如(8.3.1)的域代码为&#xff1a; { { MACROBUTTON MTPlaceRef \* MERGEFORMAT…

Redis 缓存预热、预热数据选取策略、缓存保温、性能边界

缓存预热 热点数据预热&#xff1a;根据业务分析或统计数据&#xff0c;确定热点数据&#xff08;经常被访问的数据&#xff09;&#xff0c;并将其提前加载到Redis缓存中。可以根据访问频率、访问量或其他业务指标来确定热点数据。定时预热&#xff1a;可以设置定时任务&…

Python兼职:只需要一台电脑宅在家,轻松实现月入过万!

Python兼职副业 Python是一种简单易学、高效强大的编程语言&#xff0c;正变成越来越多人选择的热门技能。不论你是否有编程基础&#xff0c;在学习Python的道路上&#xff0c;坚持每天投入2小时&#xff0c;你将看到巨大的回报。 学习Python不仅可以为你提供更多就业机会&am…

6.GodotCanvasItem、Node2D及自定义节点

CanvasItem节点 CanvasItem节点&#xff0c;CanvasItem -> Node&#xff0c;所以CanvasItem继承了Node的所有功能Canvas是画布的意思&#xff0c;所以CanvasItem代表了就是可以被绘制的节点&#xff0c;可以设置可视化界面和材质的颜色所有的2D节点和GUI节点都继承于CanvasI…

使用docker配置CCM-SLAM

一.Docker环境配置 1.拉取Docker镜像 sudo docker pull ubuntu:18.04拉取的为ununtu18版本镜像&#xff0c;环境十分干净&#xff0c;可以通过以下命令查看容器列表 sudo docker images 如果想删除多余的docker image&#xff0c;可以使用指令 sudo docker rmi -f <id&g…

openplc Linux 使用modbus RTU 从机通讯

1.Linux 环境下&#xff0c;openplc 默认使用的是modbus tcp协议通信。 想要使用串口 modbus rtu 通讯可以通过在runtime中添加SlaveDevices从机设备 2.添加设备&#xff0c;分配地址。 左边添加串口配置&#xff0c;右边是需要通讯的地址&#xff0c;从机地址都是从100开始&am…

了解光纤的最大损耗

在电信和数据传输领域&#xff0c;保持最佳的网络性能和可靠性至关重要。 影响网络完整性的关键因素之一是光纤中的信号丢失。信号损耗&#xff0c;也称为衰减损耗或光纤衰减&#xff0c;测量光缆输入和输出之间的光损耗量。本文将深入探讨光纤中的主要损耗&#xff0c;并指导您…

【python】计算水仙花数

【python】计算水仙花数 "水仙花数"是指一个3位数&#xff0c;它的三个位上的数字的3次幂之和等于它本身。例如&#xff0c;"153"就是一个水仙花数&#xff0c;因为1^3 5^3 3^3 153。以下是一个Python代码示例&#xff0c;用于计算并打印出所有的三位数…

抹机王的使用教程以及常见问题

首先请确保你已经正常安装了XPosed/EDXP/LSP框架并已激活抹机王模块&#xff0c;其中XP和EDXP模块均只需要框架内激活抹机王并重启即可&#xff0c;LSPosed注意作用域需要勾选上自己想要修改的APP&#xff08;如果你还是一意孤行只勾选系统框架那改机完全没用就是你自己的想法了…

性能测试-数据库优化二(SQL的优化、数据库拆表、分表分区,读写分离、redis、数据库监控)

数据库优化 explain select 重点&#xff1a; type类型&#xff0c;rows行数&#xff0c;extra SQL的优化 在写on语句时&#xff0c;将数据量小的表放左边&#xff0c;大表写右边where后面的条件尽可能用索引字段&#xff0c;复合索引时&#xff0c;最好按复合索引顺序写wh…