【好文翻译】JavaScript 中的 realm 是什么?

本文由体验技术团队黄琦同学翻译。

原文链接: https://weizmangal.com/2022/10/28/what-is-a-realm-in-js/

github仓库地址: https://github.com/weizman/weizman.github.io/blob/gh-pages/_posts/2020-02-02-what-is-a-realm-in-js.md

前言

作为我对浏览器 JavaScript 安全性的长期研究的一部分,在过去的一年里,我一直特别关注 realms 的安全性⭐️。

由于“基于依赖库”的开发模式的兴起,JavaScript 生态系统(尤其是浏览器里面的 JavaScript 生态系统)也就更容易受到所谓的“供应链攻击”。JavaScript 中存在一种“创建新 realm”的能力,这一能力也被成功利用来对 web 应用程序实施供应链攻击(如果你想知道为什么它能被用于实施供应链攻击,建议阅读我以前发的这篇推文)。

在“raelm 安全”这一领域,我们目前还处于任重道远的状态。我希望通过引入一款开源工具来逐步解决这个问题。这是第一款开源的 realms 安全工具——Snow-JS ❄️,作者是 LavaMoat 🌋 。这个库还在早期阶段,正在演进中,敬请期待!

为了讲清楚我做这些事情的意义,我们必须先理解“什么是 realm”。显然,我们很难用一种正确、通俗、有启发性的方式去回答这个问题。

这篇文章主要围绕浏览器中的 JavaScript 进行讨论,它也有可能通用于所有的 JavaScript 环境,这我无法保证。

realm —— JavaScript 生存的世界

你可以通俗地认为,realm 大致就是一个生态系统,JavaScript 程序就生存在这个生态系统中。就如同其他任何一个生态系统一样,它里面包含了 JavaScript 程序生存所需的各种要素。

那么,哪些东西是 JavaScript 程序所需的呢?

1) 全局执行环境

在 JavaScript 中,可以有许多不同的脚本在同一环境中运行。这些脚本可以形成作用域(scopes)。作用域是一种符合规范的执行环境,值和表达式在其中“可见”或可被访问。作用域也可以堆叠成层次结构,子作用域可以访问父作用域,反过来则不行:

<script>(function scope1() {const x = 1;(function scope2() {const y = x + 2; // 3}());const z = x + y;  // Uncaught ReferenceError: y is not defined}());
</script>

在上面的示例中,我们展示了如何使用 JavaScript 定义作用域。但是,假如我们编写了一个 JavaScript 程序,它只声明变量,而没有声明作用域,那会怎么样呢?

这被称为“顶级声明(top level declaration)”——任何没在已定义的作用域内声明或运行的东西,它们都位于默认的最外层作用域(outer-most scope)下,即全局执行环境(global execution environment)。

在这个最外层作用域下面声明的变量,它们会被全局执行环境下的不同脚本所共享:

<script> const x = 1; </script>
<script> const y = 2; </script>
<script> const z = x + y; // 3 </script>

realm 为 JavaScript 程序提供了属于自己的一个全局执行环境

上述的示例使用的是 const,它的作用是在“声明式环境(declarative environment)”中定义了一个新东西,它与 letclassmoduleimport 以及 function 声明是一类东西。

用其他方式定义出来的东西都是在“对象式环境(object environment)”中,包括 var, function, async function, function*, async function* (* 表示生成器函数)。

注意,当 JavaScript 代码在严格模式下执行以及作为模块代码执行时,不同的声明语句通过“对象式环境(object environment)”对全局对象(global object)的影响与上述解释是有偏差的!

“声明式环境(declarative environment)”和“对象式环境(object environment)”共同构成了前面提到的全局执行环境。

除了上述内容外,“对象式环境(object environment)”还提供了所有的“内置全局对象(built-in global objects)”,因为它的基础对象就是所谓的“全局对象(global object)”。

译者注:这个章节只是由于撰写需要,简单地提了一下“declarative environment”和“object environment”的概念,对定义的介绍并不完善。经过本人的核对,我认为他对“object environment”的解释甚至是错的。如果读者想要更好地理清这些东西,建议去找一些介绍 JavaScript 词法环境的资料看一看。

2) 全局对象 global object (和内部对象 intrinsic objects)

在拥有一个像样的可以执行 JavaScript 程序的环境之后,它们还需要能够执行高级操作,包括但不限于基于平台的操作。

全局对象提供了访问内置对象(built-ins)的功能,例如各种内部对象(intrinsics)、对象(object)、API 等(无论是否特定于平台)。由于这些东西的存在,全局对象的功能变得更丰富、更全面、更有用。

你可以在浏览器中通过 window 引用到全局对象,也可以在 NodeJS 中通过 global 引用到全局对象。此外还有一个通用的 globalThis 变量,它在这两个环境中都能用来访问全局对象。

首先,我们先看一下平台无关的部分。全局对象暴露了一些内置的内部对象( built-in intrinsic objects):

  1. 值(values)(例如 undefinedInfinity 等);
  2. 函数(functions) (例如 evalparseInt 等);
  3. 构造函数(constructors) (例如 BooleanDate 等);
  4. 其他(others) (例如 JSONMath 等)

除了上面那些以外,全局对象还暴露了不同平台特有的 API。

例如浏览器中就有 fetchalertdocument 等 API。

例如 DOM 就是一套众所周知的、浏览器特有的 API。它通过全局对象暴露出来。在这里,每一个 realm 都有它唯一独立的 DOM。

我们续接上面“全局执行环境”那一章节的末尾继续讲吧。全局对象除了导出那些内置对象(built-ins)之外,它还导出“对象式环境(object environment)”下声明的内容:

// `const` 声明的东西是处于“声明式环境(declarative environment)”下的
const constant = 1;// 因此它们无法通过全局对象去访问到
console.log(window.constant); // undefined// 然而,// `var` 声明的东西是处于“对象式环境(object environment)” 下的
var variable = 2;// 因此它们可以通过全局对象去访问到
console.log(window.variable); // 2

任何平台特定的对象和 API,它们都可以通过全局对象来访问。

译者注:这句话太拗口,本人翻译不出来,机翻的逻辑也是不通顺的。所以只能退而求其次,仅保留最关键的信息,以不堵塞阅读为目标。原句在这里,有缘人看到且有兴趣的话可以帮忙翻译一下:Any platform specific objects and APIs are accessible via the global object along with all intrinsic objects and new properties declared by code.

3) JavaScript 本身

最后要提的,就是在这个 realm 的执行环境中运行的 JavaScript 代码了,它也是可以与 realm 相关联的。

对执行环境(execution environment)、全局对象(global object)或在某个 realm 下产生的任何内容进行的更改/变换/更新,也都会被关联到某个唯一特定的 realm 上。

掌握“realm”的真实概念

恭喜你🎉看完了无聊的技术定义部分。接下来开始进入“不那么严肃”的内容,一切都会顺利!

1)“现实生活”中的 Realms

我们在前面强调过,realms 是一个 JavaScript 概念,并非浏览器独有。但我接下来仍会选择基于浏览器去讨论它。

现在我们已经定义了什么是领域,是时候“一睹真容”了。

在浏览器中,默认情况下只有一个 realm,即 top main realm。这就是浏览器加载的 web 应用程序所在的 realm。

正如我们刚刚学到的,Web 应用程序位于该 realm 内,该 realm 为其提供了全局执行环境(global execution environment)、最外层作用域(outer-most scope)和全局对象(global object),这个全局对象让我们得以访问各种内部对象(intrinsic objects)、平台特定 API 等。

然而,除了那个默认的 realm 以外,web 应用程序也可以创建出新的 realm,不同的 realm 是可以共存的。并且每个新的 realm 都将拥有一份独属于自己的全局执行环境、最外层作用域、全局对象…

每个 realm 都存在于一个 agent 内, 一个 agent 下可以有多个 realm。realm 则可以有子 realm 或同级 realm。

agent 将在另一篇文章中单独介绍。现在我们只需要知道,agent 是一个实体,它向自己下辖的 realm 提供不同的资源(例如事件循环)。

译者注:在 agent 之上还有 agent cluster 的概念。它们的层级关系是这样的:agent cluster -> agent -> realm。ECMAScript 规范文档里面有详细解释了这些概念。下面的内容会涉及到这几个名词,所以先在这里介绍一下。

在浏览器中,可以通过多种方式创建 realm。它们是否是同一个 agent 的子级,取决于 realm 的性质以及它们之间的关系。

这里有些例子:

  1. 同源的两个 iframe (无论是父子关系还是兄弟关系) 将在单个 agent 下形成两个 realm。
  2. 非同源的两个 iframe (无论是父子关系还是兄弟关系) 将会在不同的两个 agent 下各自形成一个 realm。(此外,为了保持跨源站点隔离,这两个 agent 甚至也是隶属于两个运行在不同进程中的不同 agent cluster 。)
  3. top main realm 和 service worker 也属于不同的两个 agent,但是这两个 agent 属于同一个 agent cluster (web worker 也是如此)。

这些关系还决定了 realm 之间可以进行何种程度的通信。

同源 iframe 的 realm 之间共享同一个事件循环,并且可以使用 contentWindow 属性,同步且自由地访问彼此的环境:

// https://example.com
const ifr = document.createElement('iframe');
ifr.src = 'https://example.com'; // same origin
ifr.onload = () => {console.log(ifr.contentWindow.document.body);// <body></body>
}
document.body.appendChild(ifr);

但跨源 iframe 的 realm 去使用这个 API 就会受到更多的访问限制:

// https://example.com
const ifr = document.createElement('iframe');
ifr.src = '//cross.origin.com'; // cross origin
ifr.onload = () => {console.log(ifr.contentWindow.document.body);// Uncaught DOMException: Blocked a frame with origin "https://example.com" from accessing a cross-origin frame.
}
document.body.appendChild(ifr);

跨源 realm 仍然可以相互通信,但通信受到更多限制,且只能基于 postMessage() 异步 API。这在 web worker、service worker 等场景下也成立。

值得一提的是,一旦著名的 shadow realms 提案落地,很快就会出现各种有趣的补充解决方案,它们可以解决此处描述的一些限制。这是一个值得持续关注的东西!

感受一下 realm 的独特性,有助于更好理解 realm 这一概念

例如,我们加载以下网站:

<html><head></head><body><iframe id="some_iframe"></iframe></body>
</html>

这么一来,里面就会有两个不同的 realm。一个是 top main realm,另一个是 iframe 里面新建的 realm。每个 realm 都有它的唯一身份标识,具有唯一的全局对象和全局执行环境:

window === some_iframe.contentWindow // false

每个 realm 都有属于自己的一组内部对象(intrinsic objects)和基于平台的 API:

window.fetch === some_iframe.contentWindow.fetch // false
window.Array === some_iframe.contentWindow.Array // false
<html><script> window.top_array = []; </script><iframe> <script> window.top.iframe_array = []; </script> </iframe><script>// top_array 和 iframe_array 诞生于不同的 realm(所以他们的原型不是同一个对象)Object.getPrototypeOf(window.iframe_array) === Object.getPrototypeOf(window.top_array) // false</script>
</html>

原始数据类型(Primitives)则不一样,即使跨了 realm,它们也是全等的。

window.Infinity === some_iframe.contentWindow.Infinity // true

2)身份不连续性

身份不连续性(identity discontinuity)是随着 realm 的存在而出现的一种特征性的状态。根据这一特征,我们能更明显的感知到 realm 的独特之处。

译者注:identity discontinuity 是一个心理学和社会科学上会使用到的术语,在 Javascript 的一些底层知识的文档和讨论中也有广泛地使用这个词来描述一种特别的现象,但本人找不到合适的中文翻译,所以使用的是机翻的结果。

为了充分地演示这个概念,我们将使用 instanceof 运算符进行演示。

想象一下,我们有一个第三方服务,它的功能是创建一个蓝色按钮。他被以 iframe 的形式加载(不要问为什么),以便 Web 应用程序可以按如下方式使用其服务:

<html><iframe id="blue_buttons_iframe"><script>window.top.createBlueButton = function(text) {const button = document.createElement('button');button.style.color = 'blue';button.value = text;return button;};</script></iframe><body><script>const blueButton = window.createBlueButton('my blue button');if (!blueButton instanceof HTMLButtonElement) {throw new Error('blue button created does not seem to actually be a button element!');}document.body.appendChild(blueButton);</script></body>
</html>

使用 instanceof,你可以判断运算符左侧的内容是否是其右侧内容的实例。例如,由于 button 元素是 HTMLButtonElement 接口的实例,所以 document.createElement('button') instanceof HTMLButtonElement 的结果是 true,而 document.createElement('div') instanceof HTMLButtonElement 的结果是 false——因为 div 元素继承自 HTMLDivElement 而不是 HTMLButtonElement,这是显而易见的。

然而,在我们的示例中,instanceof 检查将返回 false,并且将抛出自定义错误——即使 blueButton 继承自HTMLButtonElement

这太不可思议了,是吧?出现这种情况是有原因的,虽然它确实继承自 HTMLButtonElement,但总的来说,这种情况还不足以算作 “instance of”——因为,被测试的对象必须是“生下”它的那个 realm 的接口的实例

进行 instanceof 检查的最初目的,是为了确保蓝色按钮第三方服务确实提供了按钮元素,而不是其他任何元素。但实际上蓝色按钮的来源和 HTMLButtonElement 接口的来源不是同一个realm,因此 instaceof 检查将永远返回 false

上面所描述的这个错误,是由于代码中引入了身份不连续性(identity discontinuity),这表明了 realm 及其提供的一切事物是多么的独一无二。

解决身份不连续性(identity discontinuity)不见得是容易的。在上面的示例中,将检查的代码更改为 blueButton instanceof blue_buttons_iframe.contentWindow.HTMLButtonElement 是可以解决这个问题,但这不是一个可扩展的解决方案,也不是一个便利的解决方案。

为了使一个对象成为一个接口的实例,这个对象必须来自或创建于这个接口所在的 realm。

总结

我之所以整理出这些内容,是因为我没有找到任何有用、准确、易于理解的信息,没有一些现成的信息能够向我解答 “什么是realm” “realm的定义” 这些问题。为了更深入地了解 realm 在供应链攻击和安全中的作用,首先我需要充分了解 realm,这是对我来说至关重要的。我希望这些内容对你也有用。

你可以随时在 awesome-JavaScript-realms-security 这个仓库上了解我对这个领域的研究和开发。

我还建议你更多地了解 LavaMoat 🌋 开发的工具 Snow-JS ❄️ ,以进一步了解围绕 JavaScript realm 的安全防御工作。

关于 OpenTiny

图片

OpenTiny 是一套企业级 Web 前端开发解决方案,提供跨端、跨框架、跨版本的 TinyVue 组件库,包含基于 Angular+TypeScript 的 TinyNG 组件库,拥有灵活扩展的低代码引擎 TinyEngine,具备主题配置系统TinyTheme / 中后台模板 TinyPro/ TinyCLI 命令行等丰富的效率提升工具,可帮助开发者高效开发 Web 应用。


欢迎加入 OpenTiny 开源社区。添加微信小助手:opentiny-official 一起参与交流前端技术~更多视频内容也可关注B站、抖音、小红书、视频号

OpenTiny 也在持续招募贡献者,欢迎一起共建

OpenTiny 官网:https://opentiny.design/

OpenTiny 代码仓库:https://github.com/opentiny/

TinyVue 源码:https://github.com/opentiny/tiny-vue

TinyEngine 源码: https://github.com/opentiny/tiny-engine

欢迎进入代码仓库 Star🌟TinyEngine、TinyVue、TinyNG、TinyCLI~

如果你也想要共建,可以进入代码仓库,找到 good first issue标签,一起参与开源贡献~

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

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

相关文章

力扣36. 有效的数独

模拟 思路&#xff1a; 使用三个哈希表来存储数字个数 row[r][val] 用于存储第 r 行 val 1 的个数&#xff1b;column[c][val] 用于存储第 c 列 val 1 的个数&#xff1b; subboxes[i][j][val] 用于存储第 i 行、第 j 列个小九宫格 val 1 的个数&#xff0c;其中&#xff1…

rust获取本地外网ip地址的方法

大家好&#xff0c;我是get_local_info作者带剑书生&#xff0c;这里用一篇文章讲解get_local_info的使用。 get_local_info是什么&#xff1f; get_local_info是一个获取linux系统信息的rust三方库&#xff0c;并提供一些常用功能&#xff0c;目前版本0.2.4。详细介绍地址&a…

新品发布 | 多通道总线记录仪TLog1004,是你期待的吗?

新品发布 2024年1月12日&#xff0c;同星智能又发布一款多通道 CAN &#xff08;FD&#xff09;总线、LIN 总线接口logger设备&#xff0c;此款产品在TLog1002基础上进行了升级&#xff0c;同时内置 3 路数字输入和 2 路数字输出&#xff0c;便于多种信号测量和系统集成。可以满…

Dubbo源码解析第一期:如何使用Netty4构建RPC

一、背景 早期学习和使用Dubbo的时候&#xff08;那时候Dubbo还没成为Apache顶级项目&#xff09;&#xff0c;写过一些源码解读&#xff0c;但随着Dubbo发生了翻天覆地的变化&#xff0c;那些文章早已过时&#xff0c;所以现在计划针对最新的Apache Dubbo源码来进行“阅读理解…

XHCMS靶场小记(熊海)

文件包含漏洞 template下的header.php中存在文件包含漏洞&#xff08;该文件被file文件夹下的多数文件进行包含&#xff09; f参数可以包含任意文件通过php格式解析&#xff08;这是文件包含点&#xff09; 代码分析 根目录下的index.php文件&#xff1b;r参数用于获取包含文…

安捷伦AgilentE8363B网络分析仪

安捷伦AgilentE8363B网络分析仪 E8363B 是 Agilent 的 40 GHz 网络分析仪。网络分析仪是一种功能强大的仪器&#xff0c;可以以无与伦比的精度测量射频设备的线性特性。许多行业使用网络分析仪来测试设备、测量材料和监控信号的完整性 附加功能&#xff1a; 104 dB 的动态范围…

大数据导论(2)---大数据与云计算、物联网、人工智能

文章目录 1. 云计算1.1 云计算概念1.2 云计算的服务模式和类型1.3 云计算的数据中心与应用 2. 物联网2.1 物联网的概念和关键技术2.2 物联网的应用和产业2.3 大数据与云计算、物联网的关系 1. 云计算 1.1 云计算概念 1. 首先从商业角度给云计算下一个定义&#xff1a;通过网络…

升级8.0:民生手机银行的“内容解法”

数字化浪潮&#xff0c;滚滚来袭。 随着数字中国建设的持续推进&#xff0c;数字经济正在蓬勃发展。中商产业研究院分析师预测&#xff0c;2023年中国数字经济市场规模将增长至56.7万亿元&#xff0c;占GDP的比重将达到43.5%。 在此浪潮下&#xff0c;数字化的触角蔓延到各行…

洛谷NOIP2002 普及组 选数 +NOIP1999普及组 回文数

两道日常的练习题&#xff0c;废话不多说&#xff0c;直接上题上代码&#xff1a; 这道题目的难点在于怎样去根据一个不同的k值&#xff0c;通过代码来实现将所有符合题目要求的数字相加并且不重复的功能。下面请看代码&#xff0c;会有详细的讲解&#xff1a; #include<io…

用通俗易懂的方式讲解:选择最佳的 Embedding 和重排序模型,提升大模型 RAG 效果特别明显!

在构建检索增强生成&#xff08;RAG&#xff09;Pipeline时&#xff0c;一个关键组件是Retriever。我们有多种embedding模型可供选择&#xff0c;包括OpenAI、CohereAI和开源sentence transformers。此外&#xff0c;CohereAI和sentence transformers还提供了几个重排序器。 但…

OpenSource - 工具管理器easy-manager-tool

文章目录 功能说明运行配置环境配置启动docker部署 项目安全UI展示 Easy-Manager-Tool 打造软件行业首款集成工具&#xff0c;不管你是程序员&#xff0c;测试&#xff0c;运维等都可以使用该软件来提升自己的工作效率。 Easy-Manager-Tool 的诞生是为了解决软件行业众多参与者…

PGSQL安装PostGIS扩展模块

一、PostGIS简介 1、PostGIS介绍 PostGIS是一个空间数据库&#xff0c;空间数据库像存储和操作数据库中其他任何对象一样去存储和操作空间对象。 空间数据与数据库关联起来的三个要素&#xff1a;数据类型、索引和函数。 空间数据类型&#xff1a;用于指定图形为点&#xff0…

HBase学习五:运维排障

1、负载均衡 1.1 Rgion迁移 在当前的HBase版本中,Region迁移虽然是一个轻量级操作,但实现逻辑依然比较复杂,≈复杂性主要表现在两个方面:其一,Region迁移过程涉及多种状态的改变;其二,迁移过程中涉及Master、ZooKeeper(ZK)以及RegionServer等多个组件的相互协调。 …

11.什么档次的原型模式和我写的一样

在《生化危机》系列电影中&#xff0c;克隆人是个频频出现的话题。保护伞公司为了需求复制出另一个战力相当的战士Alice&#xff0c;不惜克隆成百上千个Alice&#xff0c;然而直到最后&#xff0c;非但没有真正克隆出另一个完美的Alice&#xff0c;就连Alice自己也被证实是保护…

SpringBoot(三层框架Controller,Mapper,Service)中遇到的一些注解整理

本文主要从Controller层,Service层,Mapper层这三层架构中记录用到的各种注解 还有一些MyBatis用到的注解 持续更新到本人的毕设做完为止,太多了太多了根本学不完哈哈哈 1.Controller层 1.1GetMapping/PostMapping/DeleteMapping/PutMapping 用于建立HTTP请求与处理方法之间的…

Go 爬虫之 colly 从入门到不放弃指南

文章目录 概要介绍如何学习官方文档如何安装快速开始如何配置调试分布式代理层面执行层面存储层面存储多收集器配置优化持久化存储启用异步加快任务执行禁止或限制 KeepAlive 连接扩展总结如果想用 GO 实现爬虫能力,该如何做呢?抽时间研究了 Go 的一款爬虫框架 colly。 概要…

Qt文件和目录相关操作

1.相关说明 QCoreApplication类、QFile类、QDir、QTemporaryDir类、QTemporaryFile类、QFileSystemWatcher类的相关函数 2.相关界面 3.相关代码 #include "dialog.h" #include "ui_dialog.h" #include <QFileDialog> #include <QTemporaryDir>…

Ardupilot开源飞控之VTOL之旅:打印件清单

Ardupilot开源飞控之VTOL之旅&#xff1a;打印件清单 1. 源由2. 清单2.1 模拟VTX打印件2.2 摄像头打印件2.3 GPS & RC天线打印件2.4 飞控 & 电调打印件 3. 总结4. 参考资料 1. 源由 VTOL一直仍在角落吃灰&#xff0c;主要还是手头缺点经费&#xff0c;搞台3D打印机基本…

Kotlin 移动端多平台

支持多平台编程是 Kotlin 的主要优势之一。它减少了为不同平台编写和维护相同代码所花费的时间&#xff0c;同时保留了本机编程的灵活性和优势。 1. 基本概念 KMM&#xff1a;Kotlin Multiplatform for mobile&#xff08;移动设备的 Kotlin 多平台&#xff09; KMM 多平台的主…

16k+ start 一个开源的的监控系统部署教程

安装条件 Linux或macOS系统 4GB内存 开放 33014、33174、3183端口 1.安装 1、下载源码 首先使用 git 克隆源码到本地 git clone -b main https://github.com/SigNoz/signoz.git && cd signoz/deploy/ 方式1&#xff1a;运行 install.sh 脚本一键安装 ./install.s…