How to Safely Execute Untrusted Code in Node.js: From new Function to VM and Worker Threads
This article examines reliable and trustworthy execution of dynamic JavaScript in Node.js, compares isolation techniques such as new Function, the vm module, and worker threads, evaluates their performance and security trade‑offs, and explores advanced container and WebAssembly sandboxing options.
Reliability and Trustworthy Third Parties
Reliable : safety and trust in single execution, also memory‑leak issues in repeated execution.
Trustworthy third parties : internal teams, vetted outsourced code, stable open‑source libraries (locked versions).
Relatively reliable : stable continuous operation, excludes security considerations (will be covered later).
Typical Scenarios Requiring Isolation
SSR (server‑side rendering) where component code runs on the server.
Distributed scheduled‑task systems that run lightweight user‑submitted code.
Rule engines that evaluate matching conditions.
Using container isolation under trustworthy‑third‑party constraints adds significant overhead.
Problems to Solve
Memory leaks (variable isolation).
CPU‑time limits (infinite loops, long‑running code).
External‑resource control.
Node.js Execution Model
Node.js runs on a single process with one V8 thread. Key concepts:
isolate : an independent V8 instance with its own memory management and GC, bound to an OS thread.
context : a global object inside an isolate; multiple contexts can coexist safely.
Concrete Isolation Solutions
new Function
<code>const func = new Function(`console.log('hello')`).bind({});</code>Fast for dynamic front‑end code but vulnerable to escaping the scope, e.g.:
<code>const global = Function('return this')();</code>Example of escaping:
<code>new Function('const global = Function("return this")(); return {global, a: this};').bind({})()</code>Node.js VM Module
The
vmmodule (since Node.js 0.3.0) creates context s that isolate code better than
new Function.
<code>const vm = require('vm');
const script = new vm.Script('globalVar = "set"');
const contexts = [{}, {}, {}];
contexts.forEach(context => {
script.runInNewContext(context);
});
console.log(contexts);
</code>Creating a context is relatively slow, which hurts performance for frequent executions.
Worker Threads
Introduced in Node.js 10.x, worker threads spawn separate isolates for true multithreaded execution and can be combined with
vmfor maximal isolation.
Drawbacks: slow creation, must stay resident, and communication is limited to the structured‑clone algorithm.
Performance Comparison
Solution
Variable Isolation
Memory Limit
Isolated
Time Limit
Async Limit
new Function
Partial
❌
❌
❌
❌
vm
✅
❌
❌
✅
✅
worker threads
✅
✅
✅
✅
✅
Beyond Code Isolation: Trusted Containers and WebAssembly
For malicious‑code protection, two higher‑level approaches are suggested:
Trusted containers : trimmed VMMs such as AWS Firecracker or syscall‑level isolation like Google gVisor.
WebAssembly containers : compile code to WASM, enforce instruction‑rate and memory limits via WASI; V8’s WASM support focuses on performance, not governance.
Community projects like isolated‑vm also provide isolate‑based sandboxing.
Conclusion
Choosing the right isolation technique depends on the required security level, performance budget, and deployment complexity. Simple dynamic loading can use
new Function, while stronger guarantees need
vmor worker threads, and production‑grade security may require container or WebAssembly sandboxes.
Taobao Frontend Technology
The frontend landscape is constantly evolving, with rapid innovations across familiar languages. Like us, your understanding of the frontend is continually refreshed. Join us on Taobao, a vibrant, all‑encompassing platform, to uncover limitless potential.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.