Skip to content

eval

概述

eval() 是 JavaScript 的全局函数,用于将字符串当作代码在运行时解析并执行,返回最后一条表达式的值(如果有)。

基本形式:

js
eval(codeString)

其中 codeString 必须是字符串;如果传入非字符串的值,会先被转成字符串再执行。

行为细节

  • 直接调用 eval(...)(在源代码中直接写出 eval 并调用)会在当前词法作用域中执行代码,因此能访问并修改局部变量(非严格模式下)。
  • 间接调用(例如 (0, eval)(...)window.eval(...) 或通过别名引用)会在全局作用域(或全局执行环境)中执行。
  • 在严格模式 ('use strict') 下,eval 创建的代码运行在自己的词法环境中,不能直接声明或修改外层作用域的绑定。

示例:

js
function f() {
  let a = 1
  eval('a = 2') // 在非严格模式下可能修改外层变量 a
}

const e = eval
e('var x = 5') // 间接调用,在非模块全局环境中可能创建全局变量 x

安全风险

  • 代码注入:如果 eval 的输入来源不可控(例如用户输入、第三方数据),攻击者可以执行任意 JS 代码,导致 XSS、权限提权、数据泄露等严重问题。
  • 攻击面扩大:eval 能访问运行时所有 API(包括网络、DOM、localStorage 等),因此一旦被滥用会带来全局风险。

最佳实践:尽量避免在任何上下文中执行未经严格校验的字符串。若必须执行,先对输入进行白名单校验或使用受限的执行环境(见“检测与缓解”)。

性能影响

  • 无法被 JIT 优化:eval 在运行时才解析并编译字符串,因此会阻止 JS 引擎对包含 eval 的函数做某些静态优化。
  • 解析与执行开销:每次调用都需重新解析字符串并生成字节码/机器码,代价比直接函数调用或已编译代码高。

替代方案

  • JSON 数据解析:使用 JSON.parse(而不是 eval)来解析数据结构。
  • 模板与函数生成:需要动态执行可考虑 Function 构造器,但它同样存在安全问题,优先使用更受控的方式。
  • 插件/脚本沙箱:用 iframe、Web Worker、或专门的沙箱库(如 SES、vm2(Node))隔离执行环境。
  • 动态映射:如果只是动态选择函数或属性,使用对象映射、Map 或策略模式代替动态代码生成。

常见用例与反模式

  • 反模式:用 eval 解析来自服务器的配置脚本、拼接并执行表达式、解析非 JSON 格式的数据等。
  • 可能合理的场景:开发工具、REPL、受信任并签名的动态脚本加载、或者运行于受限沙箱中的受控脚本。这些场景仍需严格控制边界并记录审计信息。

示例

错误示例(不要这样做):

js
// 用 eval 解析 JSON —— 不安全
const data = eval('(' + jsonText + ')')

安全示例:

js
// 使用 JSON.parse
const dataSafe = JSON.parse(jsonText)

// 动态调用函数表,而不是拼接执行字符串
const actions = {
  add(a, b) {
    return a + b
  },
  mul(a, b) {
    return a * b
  },
}
const name = 'add'
const res = actions[name](1, 2)

直接与间接 eval 的差异示例:

js
function direct() {
  const v = 1
  eval('v = 2') // 直接 eval 在非严格模式下可修改 v
}

function indirect() {
  const e = eval
  e('var g = 3') // 间接调用在全局作用域(非模块)创建 g
}

检测与缓解

  • 静态检测:在项目中启用 ESLint 规则 no-eval 来禁止或标记 eval 的出现。
  • 白名单解析:对于必须支持的表达式,先用解析器(如 jsep)生成 AST,再以限制良好的解释器执行,避免直接执行任意代码。
  • 沙箱执行:在 Node 使用 vm/vm2,在浏览器使用 iframeWorker 隔离不可信脚本,并限制可用 API。

调试与维护

  • 为动态执行的代码添加 //# sourceURL= 注释,便于在浏览器开发者工具中定位:
js
eval('function foo(){debugger;}\n//# sourceURL=dynamic-code.js')
  • 日志与审计:记录为何、何时以及从何处调用 eval,便于后续安全审计。

基于 MIT 许可发布