apache 不运行 node.js,仅作反向代理和静态资源托管;node.js 必须独立运行并自行实现沙箱隔离,包括 isolated-vm、权限限制、系统级约束及路径校验。

Apache 本身不运行 Node.js,它不能像处理 PHP 那样直接解析或托管 Node.js 应用。所谓“Apache 中部署 Node.js”,实际是两种常见协作模式:反向代理(推荐)或极少见的嵌入式模块(如 mod_node,已淘汰且极度不安全)。因此,“Apache 配合 Node.js 安全沙箱”不是在 Apache 里给 Node.js 加沙箱,而是让 Apache 做好边界守门人,把流量安全地转给**已在独立沙箱中运行的 Node.js 服务**。
明确职责分工:Apache 只管入口,Node.js 自己管沙箱
Apache 的角色是反向代理 + 静态资源托管 + 初始访问控制。真正的运行时隔离、权限裁剪、资源限制,必须由 Node.js 进程自身完成。强行让 Apache 干 Node.js 的活(比如试图用 suexec 或 mpm-itk 给每个请求切用户),既复杂又无效——Node.js 是长连接常驻进程,不是 CGI 式短命脚本。
- Apache 不要启用任何 Node.js 执行模块(
mod_node、mod_v8等),它们缺乏内存隔离、无超时硬限、无法剥离全局对象,存在已知逃逸路径 - 所有 Node.js 应用必须作为独立服务启动(如
node server.js或用pm2/systemd管理),并自行配置沙箱 - Apache 仅通过
ProxyPass把匹配路径(如/api/)转发到本地 Node.js 监听端口(如http://127.0.0.1:3000)
Apache 层必须做的四件事
虽不执行代码,但 Apache 是第一道防线,必须堵死绕过 Node.js 沙箱的路径:
ZenHeart zenlink 与 zenlink-mcp 为 OpenClaw,涵盖安装、MCP 注册、钩子接线、入站轮询及升级维护。
-
严格限制可代理路径:用
<location></location>显式声明哪些 URL 前缀允许代理,其余全部Require all denied。避免ProxyRequests On开启正向代理,防止被用作开放代理 -
剥离敏感请求头:用
RequestHeader unset清除X-Forwarded-For、Authorization等可能被恶意构造的头,防止 Node.js 误信伪造身份 -
强制 HTTPS 与 HSTS:所有代理流量走
https://,并在响应头添加Strict-Transport-Security,阻断中间人劫持导致的沙箱绕过 -
静态资源与动态接口物理分离:Node.js 只处理
/api/、/graphql等后端路径;前端 HTML/JS/CSS 由 Apache 直接Alias或DocumentRoot提供,不经过 Node.js —— 减少攻击面,也避免静态文件被 Node.js 沙箱错误处理
Node.js 侧沙箱必须独立落地
这才是核心。Apache 转发过来的请求,最终落在一个已加固的 Node.js 进程上。关键配置不可省略:
-
用 isolated-vm 替代 vm/vm2:处理用户上传脚本、模板渲染等动态逻辑时,必须使用
isolated-vm创建真实 V8 Isolate。设memoryLimit: 64(MB)、timeout: 1000(ms),并显式剥离process、require、Buffer、__proto__ -
启动参数级权限锁死:Node.js v20+ 启动时加
--experimental-permission --deny-net --deny-fs --allow-fs-read=/app/public --allow-env=NODE_ENV。空白白名单等于没开,--deny-net比--allow-net=更可靠 -
系统级运行约束:用 systemd 托管时,启用
DynamicUser=yes、PrivateTmp=true、NoNewPrivileges=true、MemoryMax=512M,从内核层切断提权路径 -
路径访问二次校验:即使 Apache 已限制入口,Node.js 内部所有
fs.readFile、require()、path.join()操作,都需用realpath()归一化后比对是否落在/app/白名单内,拒绝含../或空字节的输入
上线前必须验证的逃逸点
配置完不代表安全,要用典型攻击链反向打靶:
- 尝试在用户可提交的脚本中执行:
global.process.mainModule.require('child_process').execSync('id')→ 应报ReferenceError: process is not defined - 发送带
../../../etc/passwd的文件路径请求 → Node.js 层应返回 400,而非泄露内容 - 发起大量并发请求触发内存耗尽 → 进程应被 systemd 的
MemoryMax杀掉并自动重启,Apache 返回 502 而非整个服务器卡死 - 在浏览器 DevTools 中尝试
fetch('/api/internal')→ Apache 应在 Location 块中拒绝该路径,不转发给 Node.js










