通过预解析传入表达式能否让JS的eval()函数变得安全?
能否通过预解析限制
eval()的安全性? 结论:通过预解析传入eval()的代码来保证执行安全,本质上是不可行的,以下是具体原因:
1. JavaScript语法的隐式特性与歧义性,导致静态预解析无法完全覆盖
即使你禁止了for/while等显式流程控制语句,攻击者仍可通过隐式方式实现无限阻塞或恶意操作:
- 利用数组方法触发大量计算:
eval("Array(1e9).forEach(() => {})"),无显式循环但会直接耗尽浏览器资源 - 立即执行函数绕过流程控制检查:
eval("(() => { /* 恶意代码,比如窃取Cookie */ })()"),静态分析若仅检查流程控制关键字,会忽略这类匿名函数的即时执行 - 通过
Function构造函数动态生成代码:eval("Function('alert(document.cookie)')()"),若你的白名单未明确禁止Function全局对象,这类调用会直接绕过检查
2. 动态代码生成可轻松绕过静态预解析
攻击者可通过字符串拼接、变量替换等方式生成恶意代码,静态预解析无法识别动态组合后的危险内容:
- 拼接函数名绕过白名单:
eval("('al' + 'ert')(document.cookie)"),静态检查时仅能看到字符串字面量,无法预判拼接后的结果是alert调用 - 利用字符编码转换:
eval(String.fromCharCode(97,108,101,114,116,40,39,104,97,99,107,101,100,39,41)),静态解析无法直接识别编码后的恶意函数
3. 原型链与作用域逃逸可绕过变量/函数白名单限制
即使你严格限制了白名单内的变量和函数,攻击者仍可通过原型链或作用域特性访问全局对象的敏感API:
- 原型链间接获取全局构造函数:
eval("Object.prototype.constructor('alert(1)')()"),通过Object原型拿到Function构造函数,进而执行任意代码 - 利用
this指向全局对象:eval("this['document']['cookie']"),若白名单未限制this的属性访问,可直接获取DOM敏感数据 - 闭包作用域泄露:若
eval()在当前作用域执行,攻击者可通过eval("sensitiveVariable")直接读取当前作用域内未加入黑名单的敏感变量,而你无法提前预知所有作用域内的变量
4. 浏览器环境的隐式API暴露无法完全封堵
浏览器环境中存在大量可被间接调用的内置API,即使你禁用了常见的document/window,仍有隐蔽路径:
- 通过匿名函数获取全局上下文:
eval("(function(){return this;})().navigator.userAgent"),利用匿名函数的this指向全局对象,进而访问敏感信息 - 利用错误栈探测环境:
eval("new Error().stack.split('at ')[1].split('(')[0]"),可用于收集环境信息为后续攻击做准备
总结
eval()的原生性能和语言一致性确实有优势,但JavaScript的动态特性、语法复杂性以及浏览器环境的隐式暴露,导致任何预解析规则都存在被绕过的可能——你无法覆盖所有潜在的恶意代码编写方式,只要有一个遗漏点,就会导致安全风险。
内容的提问来源于stack exchange,提问作者Erik Hermansen
相关产品推荐
相关产品推荐

