了解SharedArrayBuffer和跨源隔离

3,788 阅读7分钟

JavaScript内存以二进制形式存储了程序开发和运行期间使用的每一条数据和指令。JavaScript,也被称为ECMAScript,是一种内存管理的语言。

JavaScript引擎自己访问和管理内存,它为每个程序或代码块的编写和执行分配内存。它还对内存中不再有的数据进行垃圾收集。

虽然JavaScript是一种内存管理的语言,它也帮助管理数据。但它也有缺陷。例如,JavaScript可以为一个特定的程序或变量分配超过内存中需要的自由空间。在某些情况下,JavaScript的垃圾收集会很慢。

为了让开发者能够从一个内存中分配和共享视图中的数据(使用类型化数组),引入了ArrayBufferSharedArrayBuffer 功能

什么是SharedArrayBuffer

在讨论SharedArrayBuffer ,我们可以很容易地把注意力集中在物理词上。"共享"、"阵列 "和 "缓冲区"。

数组是一种数据结构,在编程中用来存储由不同数据类型(字符串、布尔值、数字和对象)组成的数据元素。缓冲区是内存存储的一部分,用于在发送或接收使用前暂时存储数据。

ArrayBuffer是一个与其他不同的数组--它是一个字节数组,意味着只接受字节。

要在JavaScript中使用共享内存,你需要创建SharedArrayBuffer 。这是通过使用SharedArrayBuffer 对象来完成的,它创建了一个新的对象构造器,用于在多个线程之间写入和共享数据。

SharedArrayBuffer的历史

2018年1月5日,由于现代CPU架构中发现的漏洞攻击,SharedArrayBuffer 在所有主要浏览器中被禁用

此后,SharedArrayBuffer 在谷歌浏览器 v67 中被重新启用,现在可以在启用其网站隔离功能的平台上使用,我们将在本文的后续部分介绍。这一更新可以防止Spectre漏洞的攻击,使你的网站更加安全。

下面,我们将探讨如何在JavaScript中使用SharedArrayBuffer 来共享内存。首先,我们将共享内存,然后更新和同步它,最后,调试它。

在JavaScript中使用SharedArrayBuffer 来共享内存

使用SharedArrayBuffer 的好处之一是能够在JavaScript中共享内存。在JavaScript中,网络工作者是在JS代码中创建线程的一种手段。

然而,网络工作者也与SharedArrayBuffer 并行使用,它通过直接指向每个数据被存储或先前访问的内存,使网络工作者之间共享原始二进制数据。

让我们看一下如何使用SharedArrayBuffer 来共享内存的例子。

在我们开始之前,创建三个文件:index.html(我们链接脚本的地方)、script.js(主线程)和worker.js(工作者线程)。

<!--index.html file-->

<DOCTYPE html>
    <html>
    <head>
        <title>using shared array buffer</title>
        <meta charset="UTF-8">
        <meta name="sharedArrayBuffer" description="using cross-orgiin-isolation in the web browser">
        <script type="text/JavaScript" src="script.js"></script>
    </head>
    <body>
        <h3>Take a look at your browser console :)</h3>
        <script type="text/JavaScript" src="worker.js"></script>
    </body>
    </html>

让我们先看一下主线程(script.js文件)。在这里,我们访问**worker.**js,然后使用SharedArrayBuffer 对象创建一个共享内存,并将其每长度字节数设置为1024 (注意:可以使用任何需要的每长度字节数)。

使用一个类型为 [Int16Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int16Array)来解释被传递的数据,我们给类型化数组(20 )分配一个数字,以便从主线程共享。我们将缓冲区发送到工作线程,使用 [postMessage](https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage).

/*MAIN THREAD*/

const newWorker = new Worker('worker.js');
const buffMemLength = new SharedArrayBuffer(1024); //byte length
let typedArr = new Int16Array(buffMemLength);
//original data
typedArr[0] = 20;
//sending the buffer to worker 
newWorker.postMessage(buffMemLength); 

为了与工人线程共享来自主线程的数据,我们设置了一个eventListener ,以便在收到数据时运行。在这里,注意到我们正在使用一个Int16 类型的数组来显示浏览器控制台中的数据。

/*WORKER THREAD*/

addEventListener('message', ({ data }) => {
    let arr = new Int16Array(data);
    console.group('[the worker thread]');
    console.log('Data received from the main thread: %i', arr[0]);
    console.groupEnd();
    postMessage('Updated');
})

在你的浏览器控制台,你应该看到这个。

[the worker thread]                      worker.js:7
Data received from main thread: 20       worker.js:8

更新和同步共享内存

自从在JavaScript中添加了SharedArrayBuffer ,更新共享内存就变得更容易了。使用前面的例子,我们要从工人线程中更新数据。

让我们把来自主线程的原始arr[0] 设置为上面在范围内(在工人线程中)声明的dataChanged 变量。

/*WORKER THREAD*/

let BYTE_PER_LENTH = 5;
addEventListener('message', ({ data }) => {
    var arr = new Int16Array(data);
    console.group('[worker thread]');
    console.log('Data received from main thread: %i', arr[0]);
    console.groupEnd();

    //updating the data from the worker thread 
    let dataChanged = 5 * BYTE_PER_LENTH;
    arr[0] = dataChanged;
    //Sending to the main thread 
    postMessage('Updated');
})

为了让数据从工人线程中被更新,我们调用一个onmessage 事件,该事件将在主线程中触发,表明数据从工人线程中被更新。

/*MAIN THREAD*/

const newWorker = new Worker('worker.js');
const buffMemLength = new SharedArrayBuffer(1024); //byte length
var typedArr = new Int16Array(buffMemLength);
//original data
typedArr[0] = 20;
//sending the buffer to worker 
newWorker.postMessage(buffMemLength);

//onmessage event
newWorker.onmessage = (e) => {
    console.group('[the main thread]');
    console.log('Data updated from the worker thread: %i', typedArr[0]);
    console.groupEnd();
}

在你的浏览器控制台,你应该看到。

[the worker thread]                            worker.js:7
Data received from main thread: 20             worker.js:8
[the main thread]                              script.js:15
Data updated from the worker thread: 25        script.js:16

同步共享内存是很重要的,因为在实现时,同步会使共享内存同时运行而不被改变。为了在共享内存中加入同步,开发人员使用原子操作。

Atomics ,确保每个进程在下一个进程之前连续执行,在wait()notify() 方法的帮助下,所有从内存中读取或写入特定内存的数据都在一个接一个地被执行。

关于SharedArrayBuffer 和跨源隔离的最新更新

自2021年5月以来,在JavaScript中对共享内存--包括跨源隔离--进行了几次关键的更新,使开发者能够更有效地调试共享内存。目前在Firefox 79+和桌面Chrome浏览器中支持该功能,但从Chrome 92开始的更新将可用于具有跨源隔离页面的网站。

要实现SharedArrayBuffer ,你需要一个安全的环境,使用一个或多个响应头指令限制访问。这就是所谓的跨源隔离,尽管以前不鼓励使用共享内存,但事实证明这是一种更好的保护网站安全的方式。

什么是跨源隔离?

跨源隔离是一项新的安全功能(截至2021年4月),被添加到浏览器中。简而言之,它是在你的顶层文件上发送两个HTTP头信息(COOP和COEP)的结果。这些头信息使你的网站能够获得对网络API的访问,如SharedArrayBuffer ,并防止外部攻击(Spectre攻击、跨源攻击等)。

以前,使用共享内存的网站可以未经许可加载跨源内容。这些网站可以与非同一来源的窗口弹出式广告互动,有可能造成安全漏洞,或通过漏洞获取网站的用户信息。对于使用共享内存的网站来说,安全并同时保护用户信息变得非常重要。

在你的网站上启用跨源隔离功能

现在我们知道了更多关于跨源隔离的背景,让我们在网站上启用它。

首先,在你的文档的顶层启用跨原产地开放政策(COOP)的头,用same-origin

Cross-Origin-Opener-Policy: same-origin

这个标头将页面与浏览器中的任何跨源弹出窗口隔离开来,这样它们就无法访问文档或直接向其发送信息。它还可以确保你的页面与具有相同顶层来源的页面处于一个安全的环境中。

接下来,发送一个跨源头-嵌入者-政策头(COEP),其值表示require-CORP (跨源头资源政策)。

Cross-Origin-Embedder-Policy: require-corp

这可以确保所有从你的网站加载的资源都是用CORP加载的。COEP头打破了每一个需要与浏览器中的跨源窗口进行通信的整合,例如来自第三方服务器的认证和支付(结账)。

通过在你的文件的顶层设置这些标头,你的网站现在处于一个安全的环境中,并提供使用网络API的权限。

The postUnderstanding SharedArrayBuffer and cross-origin isolationappeared first onLogRocket Blog.