eval本质缺陷是执行上下文不可控,它总在调用位置词法环境中执行,能穿透访问外层变量、闭包及全局对象;最佳实践是默认禁用,例外需多重隔离,且优先采用json.parse、ast.literal_eval、function构造器等安全替代方案。

eval 的执行上下文不是“怎么设”,而是“根本不能任由它自由访问”。现代开发中,它的最佳实践就一条:**默认禁用,例外需多重隔离,且优先用替代方案**。
执行上下文不可控是 eval 的本质缺陷
eval 不是普通函数,它没有 call/apply 接口,也不接受显式传入作用域对象。它总在调用位置的词法环境里执行——这意味着它能读写外层 let 变量、闭包参数、this 绑定,甚至 document 和 localStorage。哪怕加了 "use strict",也拦不住这种穿透能力。这不是配置问题,是语言设计决定的限制。
真正可控的替代路径:Function 构造器 + 显式参数绑定
若必须动态执行含变量的表达式,应放弃 eval,改用 Function 构造器创建独立作用域:
- 把变量名作为参数名,变量值作为实参传入
- 函数体中调用 eval(此时它只访问你明确传入的参数)
- 关键防护:开头加 script = undefined;,防止用户脚本覆盖参数名
示例:const result = Function('a', 'b', 'script', 'script = undefined; return eval(script)')(10, 20, 'a + b') —— 这样 a 和 b 是安全注入的,不会污染全局,也不会意外读取外层变量。
比“控制上下文”更重要的事:先问是否真需要 eval
95% 的常见需求都有更安全、更清晰的替代方式:
- 解析 JSON:用 JSON.parse(),不接受代码,天然防注入
- 动态属性访问:用 obj[propName] 或 Reflect.get(obj, propName)
- 动态函数调用:用 obj[funcName] 或 window[funcName](前提是函数已定义)
- 公式计算:用白名单校验后走 ast.literal_eval(Python)或自定义运算解析器(JS)
不可信输入场景下,上下文隔离只是起点
即使用了 Function 构造器,如果 script 来自用户,它仍能调用 fetch、alert、localStorage.setItem。此时仅靠“作用域控制”远远不够:
- 前端轻量级隔离:放进 ,通过 postMessage 通信
- 可靠沙箱:Node.js 中用 vm2 或 isolated-vm,严格限制全局 API
- 彻底规避:把计算逻辑移到后端,在受控环境中执行并返回结果











