- 文档
- 教程
- 后端
【免费下载链接】nodebestpractices
✅ The Node.js best practices list (July 2026)
本篇技术指南围绕 nodebestpractices 安全实践清单中的核心条目「Avoid JS eval statements」展开,系统讲解eval()、setTimeout()、setInterval()与new Function()四类"字符串即代码"全局函数在 Node.js 中的注入风险、攻击原理与重构思路。读完本文,你将掌握识别高危代码执行路径的能力,并能借助仓库内配套的 lint 规则与纵深防御实践,在自己的 Node.js 服务中消除这类可致服务器被攻陷的隐患。
一、什么是"JS eval 语句":四类危险的字符串执行函数
在 Node.js 中,有一组全局函数可以接收一个字符串参数,并将该字符串当作 JavaScript 表达式、语句或语句序列来解析执行。这四类函数是:
| 函数 | 字符串参数行为 | 典型误用场景 |
|---|---|---|
eval() | 将字符串解析为 JavaScript 代码并执行 | 解析用户提交的 JSON、表达式或模板片段 |
new Function() | 以字符串构造一个新函数体并执行 | 动态生成回调函数 |
setTimeout(code, delay) | 传入字符串时按代码片段执行(而非函数引用) | 误将业务代码写成字符串传给定时器 |
setInterval(code, delay) | 同上,周期性执行字符串代码 | 误将轮询逻辑写成字符串 |
正如 avoideval.french.md 所指出的:这些函数经常在 Node.js 中被使用,其接收的字符串参数"代表一个 JavaScript 表达式、语句或语句序列"。使用它们的安全隐患在于——不受信任的用户输入可能借道进入代码执行路径。
二、攻击示例:一行字符串即可让攻击者删除整个文件系统
文档给出了一个经典且直观的恶意输入示例,请务必亲手运行验证其威力(在隔离的测试容器中):
// 攻击者可能输入的恶意代码示例 const userInput = "require('child_process').spawn('rm', ['-rf', '/'])"; // 恶意代码被执行 eval(userInput);这段代码的攻击链路可以拆解为三步:
- 攻击者向应用提交任意字符串,例如通过表单字段、查询参数、请求体或 WebSocket 消息;
- 应用未经验证、未做任何白名单过滤,直接将该字符串交给
eval(); eval()把字符串当作真实 JavaScript 代码解析执行——此时字符串内部require('child_process')成功加载了 Node.js 子进程模块,spawn('rm', ['-rf', '/'])随即在服务器上发起递归强制删除根目录的命令。
之所以说"评估用户代码本质上允许攻击者执行你能执行的任何操作",是因为:被eval()执行的代码运行在与你应用进程完全相同的权限上下文中,它拥有同样的环境变量、文件系统访问权、网络权限与进程控制能力。攻击者未必需要写入精妙的利用链——只要找到一处把用户输入喂给eval()的入口,服务器就基本宣告失守。
三、不止 eval:同类风险面盘点
值得强调的是,风险并不局限于eval()本身。仓库中相关的安全条目指出,下面这些写法同样危险:
new Function(userInput):与eval()等价地解析并执行字符串代码;setTimeout(userInput, 0)/setInterval(userInput, 1000):当第一个参数是字符串而非函数引用时,定时器会在对应时刻执行这段字符串代码;require(变量路径)动态加载模块:参见 safemoduleloading.md,如果模块路径来自用户输入,攻击者可能诱导加载任意文件:// 不安全:helperPath 变量可能已被用户输入篡改 const badWayToRequireUploadHelpers = require(helperPath); // 安全:使用字面量路径 const uploadHelpers = require('./helpers/upload');- 未净化的子进程命令拼接:参见 childprocesses.md,攻击者输入
&& rm -rf --no-preserve-root /这类 shell 元字符即可触发任意命令执行:exec('"/path/to/test file/someScript.sh" --someOption ' + input);
这些条目与本文主题互为印证:凡是在"用户输入"与"代码/命令执行"之间建立直接通道的写法,都是需要重构的高危模式。Node.js 官方对子进程 API 的告诫同样适用于eval()家族——"任何包含 shell 元字符的输入都可能被用来触发任意命令执行"。
四、重构建议:如何摆脱对 eval 家族的依赖
针对文档"建议重构代码、不再依赖这些函数"的结论,可落地的替代方案包括:
- 用
JSON.parse()替代解析 JSON 的eval():绝大多数把eval()用于"解析 JSON/配置"的场景,都可以安全替换为JSON.parse(),后者只解析数据、不执行代码; - 用函数引用替代字符串回调:
setTimeout/setInterval应始终传入具名函数或箭头函数,而非字符串代码; - 使用白名单与映射表:当确实需要根据输入选择行为时,用
switch或对象映射把有限的可选分支显式列出来,而不是动态构造代码; - 若必须执行不可信代码,请使用沙箱隔离:参见 sandbox.md,文档给出三种隔离方案——专用子进程(信息隔离快,但需限制执行时间并处理崩溃)、云 Serverless/FaaS 函数(满足全部沙箱要求但部署调用成本高)、以及
sandbox/vm2等 npm 沙箱库(一行代码即可隔离执行,但防护能力有限):const Sandbox = require('sandbox'); const s = new Sandbox(); // 语法错误被拦截 s.run('lol)hai', (output) => { console.log(output); // output='Syntax error' }); // 受限代码:进程级信息被屏蔽 s.run('process.platform', (output) => { console.log(output); // output=Null }); // 无限循环被超时终止 s.run('while (true) {}', (output) => { console.log(output); // output='Timeout' }); - 对渲染输出做转义:即便绕过了 eval,若用户输入最终拼进 HTML/CSS/JS 上下文,仍需按 escape-output.md 的指引进行转义,防止浏览器把内容当代码解释。
五、用 lint 规则自动拦截 eval 误用
人工审查难免遗漏,仓库在 lintrules.md 中给出了自动化兜底方案:为 ESLint 安装eslint-plugin-security等安全插件,它会基于已知漏洞模式对代码做静态检查,其中专门包含一条针对本文主题的规则:
detect-eval-with-expression——检测向eval()传入表达式/变量的不安全用法:
const userinput = req.body.userinput; eval(userinput); // 触发 detect-eval-with-expression 告警在 Node.js 项目上运行eslint-plugin-security后,类似上方这种"用户输入直通 eval"的写法会直接出现在检查报告中:
eslint-plugin-security 检测出不安全 eval 用法的运行结果
配合 git hooks(如pre-git)还能在代码推送到远端之前强制执行这些规则,从源头杜绝"不安全 eval 进入代码库"。这与本文的重构建议构成完整的闭环:写代码时遵循替代方案,提交代码时由 lint 把关,运行时以沙箱兜底。
六、权威观点:来自《Essential Node.js Security》的警告
《Essential Node.js Security》一书作者 Liran Tal 对eval()的定性,被 avoideval.french.md 原文引用如下:
eval() 函数或许是 JavaScript 从安全视角来看最令人皱眉的组成部分之一。它把 JavaScript 字符串解析为文本,并像 JavaScript 代码一样执行它。将它与可能流入 eval() 的不受信任用户输入混合在一起,就是一场灾难的配方——最终可能导致服务器被攻陷。
这段论述精准概括了本文的核心结论:eval()及其同族函数(new Function()、字符串形式的setTimeout/setInterval)之所以危险,不在于函数本身,而在于它把"数据"和"代码"之间的边界彻底抹平了。任何数据一旦被当作代码执行,输入验证、权限隔离、白名单等防御手段都会失去意义。
七、小结
nodebestpractices 将「避免 JS eval 语句」列为 Node.js 安全最佳实践之一,其要点可归结为:
- 识别风险面:
eval()、new Function()、字符串参数形式的setTimeout()/setInterval()都属于"字符串即代码"执行族; - 认清后果:不可信输入一旦进入这些函数,攻击者即可在应用进程权限内执行任意操作(删除文件、外传数据、横向渗透);
- 主动重构:用
JSON.parse()、函数引用、白名单映射替代动态执行;确需执行不可信代码时交给沙箱(sandbox.md); - 自动化防线:借助
eslint-plugin-security的detect-eval-with-expression规则与 git hooks 在 CI 阶段拦截(lintrules.md)。
相关实践可继续阅读仓库中的 childprocesses.md、safemoduleloading.md 与 escape-output.md,它们共同构成 Node.js 服务端"输入不可信"假设下的完整防御体系。
- 文档
- 教程
- 后端
【免费下载链接】nodebestpractices
✅ The Node.js best practices list (July 2026)
相关推荐
Node.js 安全实践:彻底避开 eval 与动态代码执行(nodebestpractices 安全指南)
Node.js 安全实践:彻底避开 eval 与动态代码执行(nodebestpractices 安全指南) eval 、 setTimeout 、 setIn
文档教程后端Node.js 安全实践:彻底规避 eval 与动态代码执行(nodebestpractices 安全指南)
Node.js 安全实践:彻底规避 eval 与动态代码执行(nodebestpractices 安全指南) 导读 在 Node.js 服务端代码中, eval
文档教程后端uMatrix脚本注入防护终极指南:如何防止恶意代码执行
uMatrix是一款强大的浏览器扩展,专门用于阻止恶意脚本注入攻击,保护您的在线安全。作为一款点选式矩阵过滤器,uMatrix能够根据请求的来源、目标和类型进行
网络安全
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考