如何在Node服务端编译运行字符串形式的JavaScript代码并获取结果
现有方案的问题
你当前使用new Function的实现仅能在本地测试使用,完全不适合线上生产环境:new Function创建的函数可以直接访问服务端进程的全局作用域,恶意用户可通过提交代码调用process、fs等原生模块,执行删除文件、窃取敏感配置、发起网络攻击等操作,会直接导致你的服务被入侵。
基于Node.js vm模块的实现方案
vm是Node.js官方提供的原生沙箱模块,支持将用户代码和主进程的全局作用域隔离,你可以自定义控制暴露给用户代码的变量和接口,安全性和可控性远高于new Function方案,参考实现如下:
const vm = require('vm'); /** * 运行用户提交的JS代码 * @param {string} userCode 用户提交的源代码 * @param {any} testInput 测试用例输入 * @returns 运行结果 */ function runJudgeCode(userCode, testInput) { // 自定义沙箱上下文,仅暴露允许用户访问的内容 const sandboxContext = { input: testInput, // 自定义console实现,收集用户代码的打印日志 console: { log: (...args) => sandboxContext.runLogs.push(args.join(' ')) }, runLogs: [] }; // 包装用户代码,按题目要求调用对应的实现函数 const wrappedCode = ` (function(input) { ${userCode} // 假设题目要求参赛者实现名为solve的函数,直接调用获取返回值 return solve(input); })(input) `; try { // 在沙箱中运行代码,设置超时和内存限制避免资源被占满 const runResult = vm.runInNewContext(wrappedCode, sandboxContext, { timeout: 3000, // 单条用例最长运行3秒 memoryLimit: 10 * 1024 * 1024 // 限制最大内存使用10MB }); return { isPass: true, output: runResult, logs: sandboxContext.runLogs }; } catch (err) { return { isPass: false, errorMsg: err.message, logs: sandboxContext.runLogs }; } } // 测试示例 console.log(runJudgeCode(`function solve(n) { return n * n; }`, 5)); // 输出:{ isPass: true, output: 25, logs: [] }
额外安全优化建议
- vm模块仍存在沙箱逃逸的可能性,如果你的服务是公开对外运营的,建议额外增加容器级隔离:每次收到代码提交就启动一个独立的轻量化Docker容器运行代码,运行结束后立即销毁容器,完全和主服务的运行环境隔离。
- 绝对不要给沙箱上下文暴露
process、fs、http等Node.js原生模块,所有用户可访问的变量都需要手动做白名单控制。 - 可根据题目难度调整超时和内存限制,避免用户提交死循环或内存泄漏代码耗尽服务器资源。
内容的提问来源于stack exchange,提问作者Sajeeb Ahamed
相关产品推荐
相关产品推荐

