微前端是如何实现作用域隔离的?

微前端是如何实现作用域隔离的?


111

一、前言

沙箱(Sandbox)是一种安全机制,目的是让程序运行在一个相对独立的隔离环境,使其不对外界的程序造成影响,保障系统的安全。作为开发人员,我们经常会同沙箱环境打交道,例如,服务器中使用 Docker 创建应用容器;使用 Codesandbox运行 Demo示例;在程序中创建沙箱执行动态脚本等。

二、使用场景

2.1 iPaaS 可视化 API 编排  

在流程编排的某些节点需要用到低代码模型转换(Transformer),用户可在转换器流程节点自定义 Groovy 脚本实现,服务端在执行自定义的 Groovy 脚本时,会放置在沙箱中,避免对整个流程逻辑造成影响。

2.2 微前端应用沙箱  

在微前端当中,有一些全局对象在所有的应用中需要共享,如 Window 对象。不同开发团队的子应用很难通过规范约束他们使用全局变量。为了保证应用的可靠性,需要技术手段去治理运行时的冲突问题;通过使用沙箱,每个前端应用都可以拥有自己的上下文环境、页面路由和状态管理,而不会相互干扰或冲突。

接下来的篇章我们将介绍大前端领域沙箱的实现以及我们如何基于JS沙箱落地应用的过程。

三、JS沙箱调研

3.1 eval和Function  

前端常见的动态执行代码的方式是使用 Eval 和 New Function 提供一个运行外部代码的环境:     

// 使用 eval 的糟糕代码:
function looseJsonParse(obj){return eval(`(${obj})`);
}
console.log(looseJsonParse("{a:(4-1), b:function(){}, c:new Date()}"
))// 使用 Function 的更好的代码:
function looseJsonParse(obj){return Function(`"use strict";return (${obj})`)();
}
console.log(looseJsonParse("{a:(4-1), b:function(){}, c:new Date()}"
))

两种方式都可以正常执行,并且返回结果相同,但是用来创建沙箱环境还不够格,因为它们都能访问[全局变量],无法实现作用域隔离。

3.2 with + new Function + proxy实现  

3.2.1 with关键字  

JavaScript 在查找某个未使用命名空间的变量时,会通过作用于链来查找,而 with 关键字,可以使得查找时,先从该对象的属性开始查找,若该对象没有要查找的属性,顺着上一级作用域链查找,若不存在要查到的属性,则会返回 ReferenceError 异常。

不推荐使用 with,在 ECMAScript 5 严格模式中该标签已被禁止。推荐的替代方案是声明一个临时变量来承载你所需要的属性。

3.2.2 ES6 Proxy  

Proxy 是 ES6 提供的新语法,Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。示例如下:

const handler = {get: function (obj, prop) {return prop in obj ? obj[prop] : 'weimob';},
};const p = new Proxy({}, handler);
p.a = 2023;
p.b = undefined;console.log(p.a, p.b); // 2023 undefined
console.log('c' in p, p.c); // false, weimob    

3.2.3 Symbol.unScopables  

With 再加上 Proxy 几乎完美解决 JS 沙箱机制。但是如果对象的Symbol.unScopables设置为 true ,会无视 with 的作用域直接向上查找,造成沙箱逃逸,所以要另外处理 Symbol.unScopables。

3.2.4 沙箱实现   

function sandbox(code, context) {context = context || Object.create(null);const fn = new Function('context', `with(context){return (${code})}`);const proxy = new Proxy(context, {has(target, key) {if (["console", "setTimeout", "Date"].includes(key)) {return true}if (!target.hasOwnProperty(key)) {throw new Error(`Illegal operation for key ${key}`)}return target[key]},get(target, key, receiver) {if (key === Symbol.unscopables) {return undefined;}return Reflect.get(target, key, receiver);}})return fn.call(proxy, proxy);
}sandbox('3+2') // 5
sandbox('console.log("智慧商业服务商")') // Cannot read property 'log' of undefined
sandbox('console.log("智慧商业服务商")', {console: window.console}) // 智慧商业服务商

上面的代码主要做了3件事,实现沙箱隔离:

  • 使用 with API,将对象添加到作用域链的顶部,变量访问会优先查找你传入的参数对象,之后再往上找;
  • 通过ES6提供的proxy,设置has函数,实现对象的访问拦截,同时处理Symbol.unscopables 的属性,控制可以被访问的变量 context,阻断沙箱内的对外访问;
  • 绑定 this 指向 proxy 对象,防止 this 访问 window;

3.3 基于iframe实现  

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

const parent = window;
const frame = document.createElement('iframe');
// 限制代码 iframe 代码执行能力
frame.sandbox = 'allow-same-origin';
document.body.appendChild(iframe);
const sandboxGlobal = iframe.contentWindow;

3.4 node运行时实现  

3.4.1 原生模块vm  

相较于浏览器环境,Node运行时就简单很多,使用其提供的原生vm模块,可以很方便的创建V8虚拟机,并在指定上下文编译和执行代码;

const vm = require('node:vm');const x = 1;const context = { x: 2 };
vm.createContext(context); // Contextify the object.const code = 'x += 40; var y = 17;';
vm.runInContext(code, context);console.log(context.x); // 42
console.log(context.y); // 17console.log(x); // 1; y is not defined.

问题来了,使用 vm.runInContext 看似创建了沙箱隔离环境,但 vm 模块足够安全吗?引用 Node 官网的回答

node:vm 模块不是安全机制。不要用它来运行不受信任的代码。

3.4.2 不安全原因  

为什么不是安全机制,继续剖析;

const vm = require('vm');
vm.runInNewContext('this.constructor.constructor("return process")().exit()');
console.log('智慧商业服务商') // 永远不会执行

这就是 JS 语言的特性,以上示例中 runInNewContext 会默认创建上下文对象, this 指向默认创建的 ctx 对象 并通过原型链的方式拿到沙盒外的 Funtion,通过Function 访问全局变量,完成逃逸,并执行逃逸后的 JS 代码。

3.4.3 解决方案  

解决方案是绑定上下文对象,同时切断上下文对象的原型链,提供纯净的上下文对象,避免通过原型链逃逸。

const vm = require('vm');
let sandBox = Object.create(null);
sandBox.title = '智慧商业服务商'
sandBox.console = console
vm.runInNewContext('console.log(title)', sandBox);

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

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

相关文章

UE5 GAS开发P35,36,37,38,39 将药水修改为AbilitySystem效果

这几节课都是将药水修改成更方便使用的AbilitySystem效果的Actor,分别为增加血量,增加蓝量,暂时获得最大生命值上限 AuraEffectActor.h // Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #…

设计模式- 策略模式(Strategy Pattern)结构|原理|优缺点|场景|示例

设计模式(分类) 设计模式(六大原则) 创建型(5种) 工厂方法 抽象工厂模式 单例模式 建造者模式 原型模式 结构型(7种) 适配器…

前端vue+xgVIdeo集成rstp流播放

注意&#xff1a;rstp流需要对应的西瓜视频插件 项目&#xff1a; petition-manager 代码概览&#xff1a; 1. video-player 子 组件 <template><div id"video-player" class"video-player"></div> </template> <script&g…

Java面试之封装、继承和多态(简洁易懂版)

一、封装&#xff1a; 1.1、什么是封装&#xff1f; 封装是指将类的某些信息隐藏在类内部&#xff0c;不允许外部直接访问&#xff0c;而是通过类提供的方法来实现对隐藏信息的操作和访问。通过封装&#xff0c;可以提高代码的安全性和可靠性。在Java中&#xff0c;使用访问修…

介绍一个开源IOT组态项目

项目介绍 金合可视化平台是一款强大而操作简便的低代码平台&#xff0c;专为满足物联网领域的可视化开发需求而设计。通过该平台&#xff0c;用户可以利用拖拽配置的方式&#xff0c;轻松创建个性化的可视化大屏&#xff0c;无需熟练的编程技能&#xff0c;大幅提高了开发效率。…

图搜索的经典启发式算法A星(A*、A Star)算法详解

文章目录 1. 引言2. 广度优先搜索3. Dijkstra 算法4. 启发式优先搜索&#xff08;Heuristic&#xff09;4.1 贪心最佳优先搜索4.2 A*搜索 1. 引言 在许多场景中&#xff0c;我们常会遇到一类问题&#xff0c;即“找到一个位置到另一个位置的距离最短&#xff08;用时最少&…

抽象类和接口有什么区别?

1.抽象类&#xff08;abstract&#xff09;是事物的共有&#xff0c;主要是继承 接口&#xff08;interface&#xff09;是定义一组规范&#xff0c;主要是实现 2.抽象类是有构造方法 接口没有构造方法 3.抽象类有抽象方法&#xff0c;也有非抽象方法 接口自从jdk8之后&#xf…

使用 Rust 后,我​​使用 Python 的方式发生了变化

使用 Rust 后&#xff0c;我​​使用 Python 的方式发生了变化 Using type hints where possible, and sticking to the classic “make illegal state unrepresentable” principle. 尽可能使用类型提示&#xff0c;并坚持经典的“使非法状态不可表示”原则。 近年来&#xff…

【Pytorch】(十三)PyTorch模型部署: TorchScript

文章目录 &#xff08;十三&#xff09;PyTorch模型部署Pytorch动态图的优缺点TorchScriptPytorch模型转换为TorchScripttorch.jit.tracetorch.jit.scripttrace和script的区别总结script 和 trace 混合使用保存和加载模型 &#xff08;十三&#xff09;PyTorch模型部署 Pytorc…

科学高效备考AMC8和AMC10竞赛,吃透2000-2024年1850道真题和解析

如何科学、有效地备考AMC8、AMC10美国数学竞赛&#xff1f;多做真题&#xff0c;吃透真题是科学有效的方法之一&#xff0c;通过做真题&#xff0c;可以帮助孩子找到真实竞赛的感觉&#xff0c;而且更加贴近比赛的内容&#xff0c;可以通过真题查漏补缺&#xff0c;更有针对性的…

jni 写日志

jni 写日志&#xff0c;每隔一分钟写一个日志文件 // 全局变量用于存储日志文件的日期和路径 std::string currentLogFile;// 获取当前日期时间的函数 std::string getCurrentDateTime() {time_t now time(0);struct tm *timeinfo localtime(&now);char buffer[80];strf…

Leetcode30-最小展台数量(66)

1、题目 力扣嘉年华将举办一系列展览活动&#xff0c;后勤部将负责为每场展览提供所需要的展台。 已知后勤部得到了一份需求清单&#xff0c;记录了近期展览所需要的展台类型&#xff0c; demand[i][j] 表示第 i 天展览时第 j 个展台的类型。 在满足每一天展台需求的基础上&am…

成功解决ImportError: cannot import name ‘builder‘ from ‘google.protobuf.internal

成功解决ImportError: cannot import name builder from google.protobuf.internal 目录 解决问题 解决思路 解决方法 解决问题 ImportError: cannot import name builder from google.protobuf.internal 解决思路 导入错误:无法从“google.protobuf.internal”导入名称“…

在React函数组件中使用错误边界和errorElement进行错误处理

在React 18中,函数组件可以使用两种方式来处理错误: 使用 ErrorBoundary ErrorBoundary 是一种基于类的组件,可以捕获其子组件树中的任何 JavaScript 错误,并记录这些错误、渲染备用 UI 而不是冻结的组件树。 在函数组件中使用 ErrorBoundary,需要先创建一个基于类的 ErrorB…

三高架构是什么

三高架构&#xff0c;也称为三高模型&#xff0c;是指高并发、高可用、高性能的系统架构模型。它是在互联网时代应运而生的一种新型的软件架构&#xff0c;主要用于解决互联网系统架构中需要面对的关键问题。 高并发&#xff1a;指系统能够处理大量并发请求的能力。在高并发场…

课时105:正则表达式_进阶知识_扩展符号

1.1.1 扩展符号 学习目标 这一节&#xff0c;我们从 基础知识、简单实践、小结 三个方面来学习 基础知识 简介 字母模式匹配[:alnum:] 字母和数字[:alpha:] 代表任何英文大小写字符&#xff0c;亦即 A-Z, a-z[:lower:] 小写字母,示例:[[:lower:]],相当于[a-z][:upper:] 大…

VS使用技巧

VS使用技巧 1、展开和缩进代码2、代码注释和取消注释 1、展开和缩进代码 缩进&#xff1a;ctrlmo 展开&#xff1a;ctrlml 2、代码注释和取消注释 注释&#xff1a;ctrlkc 取消注释&#xff1a;ctrlku

网络通信安全

一、网络通信安全基础 TCP/IP协议简介 TCP/IP体系结构、以太网、Internet地址、端口 TCP/IP协议简介如下&#xff1a;&#xff08;from文心一言&#xff09; TCP/IP&#xff08;Transmission Control Protocol/Internet Protocol&#xff0c;传输控制协议/网际协议&#xff0…

用友NC Cloud importhttpscer接口任意文件上传漏洞

声明 本文仅用于技术交流&#xff0c;请勿用于非法用途 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任。 一、漏洞描述 用友NC Cloud的importhttpscer接口如果存在任意文件上传…

搭建最新tensorflow 与pytorch环境

1、安装 Anaconda: 如果您尚未安装 Anaconda&#xff0c;首先访问 https://www.anaconda.com/products/distribution/ 下载适用于您操作系统的最新版本。按照官方指南完成安装过程。 2、设置 Conda 源 方法一&#xff1a;命令行配置 临时使用&#xff1a; 如果您只想临时为…