如何防止exec执行写入操作?多语言在线编译器Node代码执行安全问题
如何避免Node.js在线编译器中exec()修改系统文件
嘿,我刚好折腾过类似的在线编译器安全问题,针对你提到的exec()执行用户代码带来的系统文件风险,结合你已经尝试过vm和vm2的情况,给你几个落地性强的解决方案:
1. 彻底放弃直接用exec(),用vm2构建严格沙箱
你之前试的vm和vm2是对的,但如果还是用exec()去跑用户代码文件,等于完全绕开了沙箱的隔离——因为exec()是直接启动一个完整的Node进程,拥有宿主系统的权限。正确的做法是把用户代码直接放到vm2的沙箱里执行,而不是写入文件再用exec调用。
举个实用的vm2配置示例:
const { NodeVM } = require('vm2'); // 构建一个几乎完全隔离的沙箱 const vm = new NodeVM({ require: { external: false, // 禁止加载任何第三方npm包 builtin: [], // 禁用所有Node内置模块(fs、child_process全封死) root: './sandbox', // 哪怕开了模块加载,也限制在指定目录 }, sandbox: {}, // 自定义沙箱的全局对象,不暴露Node原生的global timeout: 5000, // 限制代码最多跑5秒,防死循环 memoryLimit: 128 * 1024 * 1024, // 限制内存使用,防内存溢出 }); // 直接执行用户输入的代码 try { const executionResult = vm.run(userSubmittedCode); // 返回结果给用户 } catch (error) { // 捕获并返回错误信息 }
这样用户代码完全在沙箱里跑,根本碰不到fs这类危险模块,自然没法修改系统文件。
2. 若必须用exec(),则做权限隔离+容器化
如果因为某些特殊需求(比如需要完整的Node环境支持)必须用exec()启动Node进程,那一定要做多层权限隔离:
- 创建一个低权限系统用户(比如
sandbox-runner),这个用户只拥有临时目录的读写权限,对系统核心目录(/etc、/usr等)只有只读权限,完全没有修改/删除权限。 - 把用户代码写入到
/tmp下的唯一临时目录(用crypto.randomUUID()生成唯一目录名,避免冲突),然后切换到低权限用户执行node ./temp-code.js。 - 更彻底的方式是用Docker容器:把用户代码放到隔离的容器中执行,容器挂载宿主的临时目录(只读或仅写入临时文件),系统目录全部设为只读。就算用户代码搞破坏,也只影响容器内部,不会波及宿主系统。
3. 代码预处理:提前过滤危险API
在把用户代码送入沙箱或执行之前,先做静态代码扫描,过滤掉危险代码:
- 用
@babel/parser解析用户代码的AST,检测是否有require('fs')、process、child_process.spawn这类危险调用,一旦发现直接拒绝执行。 - 也可以用简单的字符串匹配(虽然AST更可靠),比如检查代码中是否包含
fs.unlink、rm -rf这类关键词。
4. 临时文件的安全规范
如果还是需要写入临时文件,一定要遵守这些规则:
- 用
os.tmpdir()创建临时文件,并且生成唯一的文件名(比如temp-${crypto.randomUUID()}.js),避免恶意覆盖其他文件。 - 设置临时文件的权限为
600,只有当前用户能读写,防止其他进程篡改。 - 代码执行完成后立即删除临时文件,避免残留。
最后提醒一句:安全防护永远是多层的,不要只依赖单一方案。就算用了vm2,也要定期更新库版本,因为安全漏洞可能会被发现。另外,永远不要信任用户输入的任何代码,多重防护才是稳妥的。
内容的提问来源于stack exchange,提问作者Gurleen Sethi
相关产品推荐
相关产品推荐

