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

news2024/11/17 23:24:34

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

原文链接: 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.coloradmin.cn/o/1396609.html

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

相关文章

NFT Insider #117:The Sandbox 与韩剧《还魂》合作推出人物化身

引言&#xff1a;NFT Insider由NFT收藏组织WHALE Members(https://twitter.com/WHALEMembers)、BeepCrypto&#xff08;https://twitter.com/beep_crypto&#xff09;联合出品&#xff0c;浓缩每周NFT新闻&#xff0c;为大家带来关于NFT最全面、最新鲜、最有价值的讯息。每期周…

外贸建站服务器如何选?海洋建站主机推荐?

外贸建站用哪个服务器比较好&#xff1f;独立网站怎么选择主机&#xff1f; 随着全球化的趋势&#xff0c;外贸网站的建设越来越受到企业的重视。然而&#xff0c;要想让外贸网站稳定、安全、可靠地运行&#xff0c;选择合适的外贸建站服务器是关键。海洋建站将详细介绍如何选…

学习笔记-李沐动手学深度学习(一)(01-07)

个人随笔 第三列是 jupyter记事本 官方github上啥都有&#xff08;代码、jupyter记事本、胶片&#xff09; https://github.com/d2l-ai 多体会 【梯度指向的是值变化最大的方向】 符号 维度 &#xff08;弹幕说&#xff09;2&#xff0c;3&#xff0c;4越后面维度越低 4…

C语言——atoi函数解析

目录 前言 atoi函数的介绍 atoi函数的使用 atoi函数的模拟实现 前言 对于atoi函数大家可能会有些陌生&#xff0c;不过当你选择并阅读到这里时&#xff0c;请往下阅读&#xff0c;我相信你能对atoi函数熟悉该函数的头文件为<stdlib.h> 或 <cstdlib> atoi函数的…

【分布式技术】监控平台zabbix对接grafana,优化dashboard

目录 第一步&#xff1a;在zabbix server服务端安装grafana&#xff0c;并启动 第二步&#xff1a; 访问http://ip:3000/login 第三步&#xff1a;创建数据源 第四步&#xff1a;导入dashboard模板 ps&#xff1a;自定义创建新面板 第一步&#xff1a;在zabbix server服务…

C语言入门第一节-初识C语言

C语言入门第一节-初识C语言 视频教程&#xff1a;C语言教程&#xff08;全网最具有比喻形象的&#xff09;&#xff1a;持续更新ing_哔哩哔哩_bilibili 一.C语言的介绍 由C编写应用&#xff1a;Unix , Linux, MySQL都是由C編写C程序由各种令牌组成&#xff0c;令牌可以是关键宇…

P1059 [NOIP2006 普及组] 明明的随机数————C++

目录 [NOIP2006 普及组] 明明的随机数题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示 解题思路Code运行结果 [NOIP2006 普及组] 明明的随机数 题目描述 明明想在学校中请一些同学一起做一项问卷调查&#xff0c;为了实验的客观性&#xff0c;他先用计算机生成了…

LabVIEW与微信开发数字液压缸测控系统

针对传统煤矿液压支架控制存在的精度和直线度问题&#xff0c;设计了一种数字液压缸测控系统&#xff0c;其核心是LabVIEW软件与微信小程序的结合&#xff0c;以及对应的精准硬件配置。该系统使用了NI CDAQ 9189数据采集控制器、脉冲输出模块和多种传感器&#xff08;MIK-P300压…

怎么提取伴奏?推荐4个好用软件

怎么提取伴奏&#xff1f;随着音乐在日常生活中的应用越来越广泛&#xff0c;人们对音乐的需求也日益增加。其中&#xff0c;伴奏作为音乐的重要组成部分&#xff0c;往往在创作、娱乐等方面起到关键作用。那么&#xff0c;如何从各种音乐资源中提取出伴奏呢&#xff1f;推荐使…

PyTorch 中的距离函数深度解析:掌握向量间的距离和相似度计算

目录 Pytorch中Distance functions详解 pairwise_distance 用途 用法 参数 数学理论公式 示例代码 cosine_similarity 用途 用法 参数 数学理论 示例代码 输出结果 pdist 用途 用法 参数 数学理论 示例代码 总结 Pytorch中Distance functions详解 pair…

N - Rightmost Digit

题目 Given a positive integer N, you should output the most right digit of N^N. 给定一个正整数 N&#xff0c;您应该输出 N^N 的最右边的数字。 Input The input contains several test cases. The first line of the input is a single integer T which is the number o…

看完这篇我就不信还有人不懂卷积神经网络!

看完这篇我就不信还有人不懂卷积神经网络&#xff01; 前言 在深度学习大&#x1f525;的当下&#xff0c;我知道介绍卷积神经网络的文章已经在全网泛滥&#xff0c;但我还是想要写出一点和别人不一样的东西&#xff0c;尽管要讲的知识翻来覆去还是那么一些&#xff0c;但我想…

寒假思维训练计划day11

每日一题&#xff0c;这两天有事&#xff0c;断更了一天&#xff0c;今天补上&#xff0c;感觉状态也不太好&#xff0c;来道1500的题压压惊。 宣传一下我总结的几个构造题模型&#xff0c;一点个人的浅薄见解&#xff1a; 1、前后缀贪心&#xff0c;比如说观察前后缀的sum&…

关于运维·关于Zabbix监控平台的面试点

目录 引言&#xff1a;明人不说暗话&#xff0c;今天分享几个在面试的时候常被问到关于Zabbix监控平台的面试点 1、zabbix的优点 2、zabbix的缺点 3、zabbix的监控模式 4、zabbix自定义监控怎么做 5、zabbix的自动发现功能 6、zabbix分布式监控有什么特点 引言&#xff1…

QtAV:基于Qt和FFmpeg的跨平台高性能音视频播放框架

目录 一.简介 1.特性 2.支持的平台 3.简单易用的接口 二.编译 1.下载依赖包 2.开始编译 2.1克隆 2.2修改配置文件 2.3编译 三.试用 一.简介 官网地址&#xff1a;http://www.qtav.org/ Github地址&#xff1a;https://github.com/wang-bin/QtAV 1.特性 ●支持大部分…

【新书推荐】Web3.0应用开发实战(从Web 2.0到Web 3.0)

第一部分 Flask简介 第1章 安装 1.1 创建应用目录 1.2 虚拟环境 1.2.1 创建虚拟环境 1.2.2 使用虚拟环境 1.3 使用pip安装Python包 1.4 使用pipregs输出包 1.5 使用requirements.txt 1.6 使用pipenv管理包 第2章 应用的基本结构 2.1 网页显示过程 2.2 初始化 2.3 路由和视图函数…

Vue 2生命周期已达终点,正式结束使命

Vue.js是一款流行的JavaScript框架&#xff0c;拥有广泛的应用和开发者社区。自Vue.js 2发布以来&#xff0c;它在前端开发中扮演了重要角色&#xff0c;并且被广泛采用。然而&#xff0c;技术的发展是无法阻挡的&#xff0c;随着2024年的到来&#xff0c;Vue 2的生命周期也走到…

Multi-Concept Customization of Text-to-Image Diffusion——【论文笔记】

本文发表于CVPR 2023 论文地址&#xff1a;CVPR 2023 Open Access Repository (thecvf.com) Github官方代码地址&#xff1a; github.com 一、Intorduction 最近的文本到图像模型能够根据文本提示生成高质量的图像&#xff0c;可以覆盖广泛的物体、风格和场景。尽管这些模型…

Java方法及方法重载的详解

目录 一、方法的概念及使用 1.1 方法的概念 1.2 方法的定义 1.3 方法调用的执行过程 1.4 实参和形参的关系 1.5 没有返回值的方法 二、方法的重载 2.1 方法重载存在的原因 2.2 方法重载的概念 一、方法的概念及使用 1.1 方法的概念 方法就是一个代码片段. 类似于 C …

6.4.1认识Amor SWF to Video Converter2

6.4.1认识Amor SWF to Video Converter2 安装完Amor SWF to Video Converter2 (本书使用Amor SWF to Video Converter2.4.9版本)后&#xff0c;单击【开始】|【程序】|【Amor SWF to Video Converter】|【Amor SWF to Video Converter】&#xff0c;启动Amor SWF to Video Con…