1. Scenario In the previous article, Quickjs encapsulates JavaScript sandbox details, a sandbox has been implemented based on 2. Implement IJavaScriptShadowbox In fact, web worker provides an The implementation is divided into two parts, one is to implement 2.1 Implementation of the main threadimport { IJavaScriptShadowbox } from "./IJavaScriptShadowbox"; export class WebWorkerShadowbox implements IJavaScriptShadowbox { destroy(): void { this.worker.terminate(); } private worker!: Worker; eval(code: string): void { const blob = new Blob([code], { type: "application/javascript" }); this.worker = new Worker(URL.createObjectURL(blob), { credentials: "include", }); this.worker.addEventListener("message", (ev) => { const msg = ev.data as { channel: string; data: any }; // console.log('msg.data: ', msg) if (!this.listenerMap.has(msg.channel)) { return; } this.listenerMap.get(msg.channel)!.forEach((handle) => { handle(msg.data); }); }); } private readonly listenerMap = new Map<string, ((data: any) => void)[]>(); emit(channel: string, data: any): void { this.worker.postMessage({ channel: channel, data, }); } on(channel: string, handle: (data: any) => void): void { if (!this.listenerMap.has(channel)) { this.listenerMap.set(channel, []); } this.listenerMap.get(channel)!.push(handle); } offByChannel(channel: string): void { this.listenerMap.delete(channel); } } 2.2 Implementation of web worker threadsimport { IEventEmitter } from "./IEventEmitter"; export class WebWorkerEventEmitter implements IEventEmitter { private readonly listenerMap = new Map<string, ((data: any) => void)[]>(); emit(channel: string, data: any): void { postMessage({ channel: channel, data, }); } on(channel: string, handle: (data: any) => void): void { if (!this.listenerMap.has(channel)) { this.listenerMap.set(channel, []); } this.listenerMap.get(channel)!.push(handle); } offByChannel(channel: string): void { this.listenerMap.delete(channel); } init() { onmessage = (ev) => { const msg = ev.data as { channel: string; data: any }; if (!this.listenerMap.has(msg.channel)) { return; } this.listenerMap.get(msg.channel)!.forEach((handle) => { handle(msg.data); }); }; } destroy() { this.listenerMap.clear(); onmessage = null; } } 3. Use WebWorkerShadowbox/WebWorkerEventEmitterMain thread code const shadowbox: IJavaScriptShadowbox = new WebWorkerShadowbox(); shadowbox.on("hello", (name: string) => { console.log(`hello ${name}`); }); // The code here refers to the code of the web worker thread below shadowbox.eval(code); shadowbox.emit("open"); Web worker thread code const em = new WebWorkerEventEmitter(); em.on("open", () => em.emit("hello", "liuli")); The following is a schematic diagram of the code execution flow; 4. Limit web worker global API As
In fact, There is an article that explains how to perform side-channel attacks on the web through // whitelistWorkerGlobalScope.ts /** * Set the web worker runtime whitelist to ban all unsafe APIs */ export function whitelistWorkerGlobalScope(list: PropertyKey[]) { const whitelist = new Set(list); const all = Reflect.ownKeys(globalThis); all.forEach((k) => { if (whitelist.has(k)) { return; } if (k === "window") { console.log("window: ", k); } Reflect.deleteProperty(globalThis, k); }); } /** * Whitelist of global values */ const whitelist: ( | keyof typeof global | keyof WindowOrWorkerGlobalScope | "console" )[] = [ "globalThis", "console", "setTimeout", "clearTimeout", "setInterval", "clearInterval", "postMessage", "onmessage", "Reflect", "Array", "Map", "Set", "Function", "Object", "Boolean", "String", "Number", "Math", "Date", "JSON", ]; whitelistWorkerGlobalScope(whitelist); Then execute the above code before executing the third-party code import beforeCode from "./whitelistWorkerGlobalScope.js?raw"; export class WebWorkerShadowbox implements IJavaScriptShadowbox { destroy(): void { this.worker.terminate(); } private worker!: Worker; eval(code: string): void { // This line is the key const blob = new Blob([beforeCode + "\n" + code], { type: "application/javascript", }); // Other code. . . } } Since we use ts to write source code, we must also package ts into import { defineConfig, Plugin } from "vite"; import reactRefresh from "@vitejs/plugin-react-refresh"; import checker from "vite-plugin-checker"; import { build } from "esbuild"; import * as path from "path"; export function buildScript(scriptList: string[]): Plugin { const _scriptList = scriptList.map((src) => path.resolve(src)); async function buildScript(src: string) { await build({ entryPoints: [src], outfile: src.slice(0, src.length - 2) + "js", format: "iife", bundle: true, platform: "browser", sourcemap: "inline", allowOverwrite: true, }); console.log("Build completed: ", path.relative(path.resolve(), src)); } return { name: "vite-plugin-build-script", async configureServer(server) { server.watcher.add(_scriptList); const scriptSet = new Set(_scriptList); server.watcher.on("change", (filePath) => { // console.log('change: ', filePath) if (scriptSet.has(filePath)) { buildScript(filePath); } }); }, async buildStart() { // console.log('buildStart: ', this.meta.watchMode) if (this.meta.watchMode) { _scriptList.forEach((src) => this.addWatchFile(src)); } await Promise.all(_scriptList.map(buildScript)); }, }; } // https://vitejs.dev/config/ export default defineConfig({ plugins: [ reactRefresh(), checker({ typescript: true }), buildScript([path.resolve("src/utils/app/whitelistWorkerGlobalScope.ts")]), ], }); Now, we can see that the global APIs in 5. The main advantages of web worker sandbox You can use This is the end of this article about the details of WebWorker encapsulating JavaScript sandbox. For more related content about WebWorker encapsulating JavaScript sandbox, please search for previous articles on 123WORDPRESS.COM or continue to browse the related articles below. I hope everyone will support 123WORDPRESS.COM in the future! You may also be interested in:
|
<<: Understand CSS3 Grid layout in 10 minutes
>>: Why MySQL does not recommend using subqueries and joins
CSS sets Overflow to hide the scroll bar while al...
This article shares the specific code of JavaScri...
This article uses examples to illustrate common b...
Table of contents Single condition single data fi...
Learning objectives: Learn to use Windows system ...
this keyword Which object calls the function, and...
1) Enter the folder path where the jdk file is st...
First, let me talk about the general idea: 1. Log...
This article example shares the specific code of ...
Table of contents 1. Initialize the map 2. Map Po...
This article example shares the specific code of ...
1. Environment version Docker version 19.03.12 ce...
Preface Usually when making h5 pages, you need to...
1. Introduction to mysqlbackup mysqlbackup is the...
This article records the creation of a USB boot d...