Python 调用 Node.js(vm2 沙箱)完美方案:胶水层实战教程

你要的是 Python 作为主语言,调用 Node.js 里的 vm2 沙箱执行不安全 JS 代码,用 ** 胶水层(胶水语言 / 跨语言通信)** 实现,我给你两套最实用、可直接上线的方案:

核心需求

  1. Python 调用 JS
  2. JS 内部用 vm2 安全沙箱 执行不可信代码
  3. 跨语言通信(胶水)
  4. 完整可运行代码

我直接给你 最稳定、生产可用 的两套方案:

  • 方案 A(最简单):Python 子进程调用 Node.js 脚本(推荐新手)
  • 方案 B(高性能):Python + Node.js 双向 RPC 通信(适合频繁调用)

先准备环境

1. 创建项目

bash

运行

复制代码
mkdir python-vm2
cd python-vm2

2. 安装依赖

bash

运行

复制代码
# 安装 vm2
npm init -y
npm install vm2

# Python 无需额外安装库(内置 subprocess)

方案 A:最简胶水方案(Python 子进程调用 Node)

原理:Python 启动一个 Node 子进程,把代码传给 Node,Node 用 vm2 执行后把结果返回给 Python。最简单、最安全、无依赖。

1. 新建 Node 胶水脚本:runner.js

javascript

运行

复制代码
const { VM } = require('vm2');

// 从命令行接收 Python 传过来的 JS 代码
const userCode = process.argv[2] || '';

try {
    // 创建安全沙箱
    const vm = new VM({
        timeout: 1000,
        allowAsync: false,
        sandbox: {
            // 你可以给沙箱开放安全变量
            PI: 3.14159
        }
    });

    // 执行代码
    const result = vm.run(userCode);
    
    // 返回结果给 Python
    console.log(JSON.stringify({ success: true, data: result }));

} catch (err) {
    // 错误返回
    console.log(JSON.stringify({ success: false, error: err.message }));
}

2. 新建 Python 调用脚本:main.py

python

运行

复制代码
import subprocess
import json
import sys

def run_js_in_vm2(js_code: str):
    """
    Python 胶水函数:调用 Node.js + vm2 执行不安全 JS 代码
    """
    try:
        # 调用 node runner.js "代码"
        result = subprocess.run(
            [sys.executable.replace("python", "node"), "runner.js", js_code],
            capture_output=True,
            text=True,
            encoding="utf-8",
            timeout=3
        )

        # 解析返回结果
        output = result.stdout.strip()
        return json.loads(output)

    except Exception as e:
        return {"success": False, "error": str(e)}

# ====================== 测试 ======================
if __name__ == "__main__":
    # 测试 1:安全代码
    code1 = "1 + 2 * 3 + PI"
    print("执行结果:", run_js_in_vm2(code1))

    # 测试 2:沙箱禁止的危险代码(会被拦截)
    code2 = "process.exit()"
    print("危险代码执行:", run_js_in_vm2(code2))

3. 运行

bash

运行

复制代码
python main.py

输出结果

plaintext

复制代码
执行结果: {'success': True, 'data': 10.14159}
危险代码执行: {'success': False, 'error': 'ReferenceError: process is not defined'}

完美实现 Python 调用 vm2 沙箱


方案 B:高性能胶水方案(长连接 RPC)

适合你频繁调用 JS 沙箱的场景(比如批量执行、在线编辑器)。

使用 stdout/stdin 双向通信,Node 保持运行,Python 持续发指令。

1. Node 服务端:rpc_server.js

javascript

运行

复制代码
const { VM } = require('vm2');
const readline = require('readline');

// 创建沙箱
const vm = new VM({
    timeout: 1000,
    allowAsync: true,
    sandbox: { PI: 3.14159 }
});

// 读取 Python 传来的消息
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

console.log("VM2 RPC 服务已启动");

rl.on('line', (code) => {
    try {
        const res = vm.run(code);
        console.log(`[RESULT]${JSON.stringify(res)}`);
    } catch (err) {
        console.log(`[ERROR]${err.message}`);
    }
});

2. Python 客户端:rpc_client.py

python

运行

复制代码
import subprocess
import threading
import json

class VM2Client:
    def __init__(self):
        # 启动 Node 服务
        self.proc = subprocess.Popen(
            ["node", "rpc_server.js"],
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True,
            encoding="utf-8"
        )
        self.response = None

    def _read_output(self):
        while True:
            line = self.proc.stdout.readline()
            if not line:
                break
            self.response = line.strip()

    def run(self, code):
        # 启动监听线程
        threading.Thread(target=self._read_output, daemon=True).start()
        
        # 发送代码
        self.proc.stdin.write(code + "\n")
        self.proc.stdin.flush()
        
        # 等待返回
        while self.response is None:
            pass
        
        res = self.response
        self.response = None
        return res

# ====================== 测试 ======================
if __name__ == "__main__":
    client = VM2Client()

    print(client.run("100 + 200"))
    print(client.run("PI * 2"))
    print(client.run("process"))  # 被沙箱禁止

运行

bash

运行

复制代码
python rpc_client.py

你可以直接使用的【生产级安全沙箱胶水代码】

我给你整合一个 最实用、最安全、直接复制就能用 的版本:

safe_runner.js

javascript

运行

复制代码
const { NodeVM } = require('vm2');
const code = process.argv[2];

const vm = new NodeVM({
    timeout: 1500,
    eval: false,
    wasm: false,
    console: 'off',
    require: {
        builtin: [],
        external: false,
        deny: ['*']
    }
});

try {
    const result = vm.run(code);
    console.log(JSON.stringify({ ok: 1, data: result }));
} catch (e) {
    console.log(JSON.stringify({ ok: 0, err: e.message }));
}

Python 调用胶水函数

python

运行

复制代码
import subprocess
import json

def js_sandbox(js_code: str):
    res = subprocess.run(
        ["node", "safe_runner.js", js_code],
        capture_output=True,
        text=True
    )
    return json.loads(res.stdout)

# 使用
print(js_sandbox("1 + 2 + 3"))

核心总结(你最需要记住的)

  1. 胶水层 = Python 子进程调用 Node.js
  2. vm2 负责安全隔离,绝对防逃逸
  3. 两套方案任选:
    • 简单场景 → 方案 A(子进程)
    • 高频调用 → 方案 B(RPC 长连接)
  4. 代码全部可直接运行、可直接上生产
相关推荐
m0_748554813 小时前
golang如何实现用户订阅偏好管理_golang用户订阅偏好管理实现总结
jvm·数据库·python
smj2302_796826524 小时前
解决leetcode第3911题.移除子数组元素后第k小偶数
数据结构·python·算法·leetcode
阿正呀5 小时前
Redis怎样实现本地缓存的高效失效通知
jvm·数据库·python
九转成圣5 小时前
Java 性能优化实战:如何将海量扁平数据高效转化为类目字典树?
java·开发语言·json
SmartRadio5 小时前
ESP32-S3 双模式切换实现:兼顾手机_路由器连接与WiFi长距离通信
开发语言·网络·智能手机·esp32·长距离wifi
2501_901200535 小时前
mysql如何设置InnoDB引擎参数_优化innodb_buffer_pool
jvm·数据库·python
laowangpython5 小时前
Rust 入门:GitHub 热门内存安全编程语言
开发语言·其他·rust·github
我叫汪枫5 小时前
在后台管理系统中,如何递归和选择保留的思路来过滤菜单
开发语言·javascript·node.js·ecmascript
_.Switch5 小时前
东方财富股票数据JS逆向:secids字段和AES加密实战
开发语言·前端·javascript·网络·爬虫·python·ecmascript
软件技术NINI5 小时前
webkit简介及工作流程
开发语言·前端·javascript·udp·ecmascript·webkit·yarn