Flowise 的沙箱安全:vm2 隔离、E2B 回退与安全 HTTP 封装
学习目标
- 理解 Flowise 的三层沙箱安全模型:vm2 本地隔离、E2B 云端沙箱、HTTP 安全封装
- 掌握 NodeVM 的配置选项与安全边界
- 了解 SSRF 防护的 hostname deny list 机制
前置知识
本章为项目特定的工程实践,无外部前置知识要求。
项目实践
三层沙箱模型
第一层:vm2 NodeVM 隔离
executeJavaScriptCode 函数是代码执行的核心:
const defaultNodeVMOptions = { console: 'inherit', // 继承控制台输出 sandbox, // 沙箱变量 require: { external: { modules: deps, // 允许的 npm 模块白名单 transitive: false // 禁止传递依赖 }, builtin: builtinDeps, // 内置模块白名单 mock: secureWrappers // 替换 HTTP 库为安全封装 }, eval: false, // 禁止 eval wasm: false, // 禁止 WebAssembly timeout: timeoutMs // 超时控制(默认 5 分钟)}
const vm = new NodeVM(finalNodeVMOptions)const response = await vm.run(`module.exports = async function() {${code}}()`)沙箱对象:阻断危险模块
const sandbox = { $input: input, // 输入数据 $vars: preparedVars, // 全局变量 $flow: flowConfig, // 流程配置(sessionId, chatId 等) util: undefined, // 禁止 util Symbol: undefined, // 禁止 Symbol child_process: undefined, // 禁止子进程 fs: undefined, // 禁止文件系统 process: undefined // 禁止进程对象}第二层:E2B 云端沙箱
当设置了 E2B_APIKEY 环境变量时,Flowise 自动切换到 E2B 云端沙箱:
if (useSandbox && process.env.E2B_APIKEY) { → 使用 E2B Sandbox(完全隔离的容器)} else { → 使用 vm2 NodeVM(进程内隔离)}E2B 沙箱的优势:
- 完全隔离的容器,vm2 已知漏洞(CVE-2023-29017)无法利用
- 独立的文件系统和网络环境
- 可配置的资源限制(CPU、内存、网络)
E2B 沙箱的执行方式:
- 将用户代码序列化为字符串
- 通过 E2B API 发送到远程容器
- 在容器中执行,返回结果
第三层:HTTP 安全封装
vm2 中的 HTTP 请求(axios、node-fetch)被替换为安全封装:
secureWrappers['axios'] = async (config) => { await checkDenyList(config.url) // 检查 hostname deny list return secureAxiosWrapper(config) // 使用安全 fetch}
secureWrappers['node-fetch'] = async (url, options) => { return secureFetch(url, options) // 安全 fetch}secureFetch 的防护措施:
- 检查 URL 是否为内网地址(SSRF 防护)
- 跟随重定向时重新检查目标地址
- 限制请求方法和头信息
checkDenyList:
- 阻止访问已知不安全的 hostname
- 阻止访问内网地址(127.0.0.1、10.x.x.x、192.168.x.x 等)
问题与规避
vm2 的已知漏洞
问题:vm2 存在多个已知逃逸漏洞(如 CVE-2023-29017),攻击者可能通过精心构造的代码逃逸沙箱。
对策:
- 不要在生产环境中执行不受信任的代码
- 启用 E2B 云端沙箱作为安全回退
- 限制可用的 npm 模块(
require.external.modules白名单) - 设置合理的 timeout(防止无限循环消耗资源)
SSRF 攻击向量
问题:用户自定义函数可能尝试访问内网服务(如 http://169.254.169.254 获取 AWS 元数据)。
对策:
- 所有 HTTP 请求通过
secureFetch,自动检测并阻止内网地址 checkDenyList维护已知的危险 hostname 列表- 禁止使用原始的
http/https模块(不在白名单中)
资源耗尽
问题:恶意代码可能通过无限循环或大内存分配耗尽服务器资源。
对策:
- NodeVM 的
timeout选项限制执行时间(默认 5 分钟) - 可通过
SANDBOX_TIMEOUT环境变量覆盖 - E2B 沙箱有独立的资源限制配置
设计取舍
vm2 vs 其他沙箱方案
| 方案 | 安全性 | 性能 | 复杂度 |
|---|---|---|---|
| vm2 NodeVM | 中(有已知漏洞) | 高(进程内) | 低 |
| E2B Sandbox | 高(容器隔离) | 中(网络延迟) | 高(需要 API Key) |
| Node.js vm | 低(无隔离) | 最高 | 最低 |
| Web Workers | 高(浏览器隔离) | 中 | 中 |
| Docker 容器 | 最高 | 低(容器启动) | 高 |
Flowise 的 vm2 + E2B 双策略是务实的选择:
- 开发环境/可信代码:vm2 足够,零延迟
- 生产环境/不可信代码:E2B 提供完全隔离
模块白名单 vs 完全隔离
| 方案 | 优势 | 代价 |
|---|---|---|
| 白名单 | 灵活,允许有用的模块 | 需要维护白名单,可能遗漏危险模块 |
| 完全隔离 | 最安全 | 用户代码功能受限,实用性低 |
Flowise 选择白名单,因为:
- 用户需要
node-fetch、axios等 HTTP 库调用 API lodash、moment等工具库在数据处理中常用- 通过安全封装(
secureWrappers)可以在保留功能的同时防护风险