首页 文章

为什么使用JavaScript eval函数是一个坏主意?

提问于
浏览
487

eval函数是一种动态生成代码的强大而简单的方法,那么有什么警告呢?

25 回答

  • 2

    我想到两点:

    • 安全性(但只要您自己生成要评估的字符串,这可能不是问题)

    • 性能:直到要执行的代码未知,才能进行优化 . (关于javascript和性能,当然Steve Yegge's presentation

  • 6

    这是一种可能的安全风险,它具有不同的执行范围,并且效率非常低,因为它为代码的执行创建了一个全新的脚本环境 . 有关更多信息,请参见此处:eval .

    但它非常有用,并且适度使用可以添加许多好的功能 .

  • 20

    如果你发现你的代码中使用了eval(),那么请记住“eval()是邪恶的” .

    此函数接受任意字符串并将其作为JavaScript代码执行 . 当事先知道有问题的代码(未在运行时确定)时,没有理由使用eval() . 如果代码是在运行时动态生成的,那么通常有一种更好的方法来实现没有eval()的目标 . 例如,只使用方括号表示法来访问动态属性更好更简单:

    // antipattern
    var property = "name";
    alert(eval("obj." + property));
    
    // preferred
    var property = "name";
    alert(obj[property]);
    

    使用 eval() 也有安全隐患,因为您可能正在执行已被篡改的代码(例如来自网络) . 当处理来自Ajax请求的JSON响应时,这是一个常见的反模式 . 在这些情况下,最好使用浏览器的内置方法来解析JSON响应,以确保它安全有效 . 对于本机不支持 JSON.parse() 的浏览器,您可以使用JSON.org中的库 .

    同样重要的是要记住,将字符串传递给 setInterval()setTimeout()Function() 构造函数在很大程度上类似于使用 eval() ,因此应该避免 .

    在幕后,JavaScript仍然必须评估并执行您作为编程代码传递的字符串:

    // antipatterns
    setTimeout("myFunc()", 1000);
    setTimeout("myFunc(1, 2, 3)", 1000);
    
    // preferred
    setTimeout(myFunc, 1000);
    setTimeout(function () {
    myFunc(1, 2, 3);
    }, 1000);
    

    使用新的Function()构造函数类似于eval(),应小心处理 . 它可能是一个强大的构造,但经常被误用 . 如果绝对必须使用 eval() ,则可以考虑使用新的Function() .

    由于在新的Function()中计算的代码将在本地函数作用域中运行,因此有一个很小的潜在好处,因此在被评估的代码中使用var定义的任何变量都不会自动成为全局变量 .

    防止自动全局变量的另一种方法是将 eval() 调用包装成立即函数 .

  • 5

    这可能会成为一个问题,因为下一代浏览器会出现一些JavaScript编译器 . 通过Eval执行的代码可能无法针对这些较新的浏览器执行其他JavaScript . 有人应该做一些剖析 .

  • 0

    主要是's a lot harder to maintain and debug. It'就像一个 goto . 您可以使用它,但它使得更难以找到问题,并且对于可能需要稍后进行更改的人员更加困难 .

  • 5

    将用户输入传递给eval()存在安全风险,但每次调用eval()都会创建JavaScript解释器的新实例 . 这可能是资源匮乏 .

  • 4

    JavaScript Engine在编译阶段执行了许多性能优化 . 其中一些归结为能够基本上静态地分析代码,并预先确定所有变量和函数声明的位置,以便在执行期间解析标识符所需的工作量更少 .

    但是如果引擎在代码中找到了一个eval(..),它基本上必须假设它对标识符位置的所有意识都可能是无效的,因为它无法在最近的时间知道你可以传递给eval(...)的代码 . 修改词法范围,或者您可以传递给的对象的内容,以创建一个新的词法范围进行查阅 .

    换句话说,在悲观意义上,如果存在eval(..),那么它所做的大多数优化都是没有意义的,因此根本不执行优化 .

    这解释了一切 .

    参考:

    https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20&%20closures/ch2.md#eval

    https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20&%20closures/ch2.md#performance

  • 2

    我不会试图反驳此前所说的任何内容,但我会提供eval()的使用(据我所知)不能以任何其他方式完成 . 可能还有其他方法可以对此进行编码,也可能是对其进行优化的方法,但这样做是为了清晰起见而没有任何花边和口哨,以说明eval的使用,实际上没有任何其他选择 . 即:动态(或更准确地)以程序方式创建的对象名称(与值相对) .

    //Place this in a common/global JS lib:
    var NS = function(namespace){
        var namespaceParts = String(namespace).split(".");
        var namespaceToTest = "";
        for(var i = 0; i < namespaceParts.length; i++){
            if(i === 0){
                namespaceToTest = namespaceParts[i];
            }
            else{
                namespaceToTest = namespaceToTest + "." + namespaceParts[i];
            }
    
            if(eval('typeof ' + namespaceToTest) === "undefined"){
                eval(namespaceToTest + ' = {}');
            }
        }
        return eval(namespace);
    }
    
    
    //Then, use this in your class definition libs:
    NS('Root.Namespace').Class = function(settings){
      //Class constructor code here
    }
    //some generic method:
    Root.Namespace.Class.prototype.Method = function(args){
        //Code goes here
        //this.MyOtherMethod("foo"));  // => "foo"
        return true;
    }
    
    
    //Then, in your applications, use this to instantiate an instance of your class:
    var anInstanceOfClass = new Root.Namespace.Class(settings);
    

    编辑:顺便说一句,我不建议(出于前面指出的所有安全原因)你在用户输入上 Build 对象名称 . 我无法想象你有什么理由想要这样做 . 不过,我想我会指出它不是一个好主意:)

  • 2

    这是关于eval的好文章之一它是如何不是邪恶的:http://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/

    我不是说你应该跑出去并开始在各地使用eval() . 事实上,运行eval()的好用例很少 . 代码清晰度,可调试性以及当然不应忽视的性能肯定存在问题 . 但是当你有一个eval()有意义的情况时,你不应该害怕使用它 . 尽量不要先使用它,但是当适当使用eval()时,不要让任何人吓唬你认为你的代码更脆弱或更不安全 .

  • 7

    如果您希望用户输入一些逻辑函数并评估AND的OR,则JavaScript eval函数是完美的 . 我可以接受两个字符串和 eval(uate) string1 === string2 等 .

  • 17

    除非您100%确定所评估的代码来自可靠来源(通常是您自己的应用程序),否则这是将您的系统暴露给跨站点脚本攻击的绝对方式 .

  • 2

    我知道这个讨论已经过时了,但我真的很喜欢谷歌的做法,并希望与他人分享这种感觉;)

    另一件事是越多越好你得到越多你试图理解,最后你只是因为有人这么说而不相信某事好或坏:)这是一个非常鼓舞人心的video帮助我更多地思考自己:)好的做法很好,但不要盲目使用它们:)

  • 1

    它大大降低了您对安全性的信心 .

  • 4

    我相信这是因为它可以从字符串中执行任何JavaScript函数 . 使用它可以让人们更容易将恶意代码注入应用程序 .

  • 13

    除了可能的安全问题,如果您正在执行用户提交的代码,大多数情况下,有一种更好的方法,不涉及每次执行时重新解析代码 . 匿名函数或对象属性可以替换eval的大多数用法,并且更安全,更快速 .

  • 360

    eval()非常强大,可用于执行JS语句或计算表达式 . 但是这个问题与eval()函数有很大关系,但是这篇文章有很好的信息:http://blogs.popart.com/2009/07/javascript-injection-attacks/如果你正在寻找eval()的基础知识,请看这里:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval

  • 1

    要记住的一件事是,您通常可以使用eval()在受限制的环境中执行代码 - 阻止特定JavaScript函数的社交网站有时会被错误地在eval块中分解 -

    eval('al' + 'er' + 't(\'' + 'hi there!' + '\')');
    

    因此,如果您正在寻找运行某些JavaScript代码,否则可能无法使用它(Myspace,我正在看着您......),那么eval()可能是一个有用的技巧 .

    但是,由于上面提到的所有原因,你不应该将它用于你自己的代码,你可以完全控制它 - 它只是没有必要,并且更好地降级到'棘手的JavaScript黑客'架子 .

  • 1
    • __12798_的错误使用会打开您的注射攻击代码

    • Debugging 可能更具挑战性(没有行号等)

    • eval'd代码执行速度较慢(无法编译/缓存eval'd代码)

    编辑:正如@Jeff Walden在评论中指出的那样,#3今天不像2008年那么真实 . 但是,虽然编译脚本的某些缓存可能会发生,但这仅限于重复进行eval重复的脚本 . 更可能的情况是,您正在评估每次都经过轻微修改的脚本,因此无法缓存 . 我们只是说一些eval'd代码执行得更慢 .

  • 35

    如果你知道你正在使用它的上下文,它不一定是那么糟糕 .

    如果您的应用程序使用 eval() 从某个JSON创建一个对象,该对象已从XMLHttpRequest返回到您自己的站点,由您的可信服务器端代码创建,则可能不是问题 .

    不受信任的客户端JavaScript代码可以't do that much anyway. Provided the thing you'重新执行 eval() 来自一个合理的来源,你很好 .

  • 15

    我甚至会说,如果你在浏览器中运行的javascript中使用 eval() 并不重要 . *(警告)

    所有现代浏览器都有一个开发人员控制台,您可以在其中执行任意javascript,任何半智能开发人员都可以查看您的JS源代码并将他们需要的任何内容放入开发控制台中以执行他们想要的操作 .

    *只要您的服务器 endpoints 具有用户提供的值的正确验证和清除,在客户端javascript中解析和评估的内容应该无关紧要 .

    如果您要问是否适合在PHP中使用 eval() ,答案是 NO ,除非您 whitelist 可以传递给您的eval语句的任何值 .

  • 4

    这并不总是一个坏主意 . 以代码生成为例 . 我最近写了一个名为Hyperbars的库,它弥合了virtual-domhandlebars之间的差距 . 它通过解析把手模板并将其转换为随后由virtual-dom使用的hyperscript来实现 . 超文本生成为一个字符串首先在返回之前, eval() 它将它变成可执行代码 . 我发现 eval() 在这种特殊情况下与邪恶完全相反 .

    基本上来自

    <div>
        {{#each names}}
            <span>{{this}}</span>
        {{/each}}
    </div>
    

    对此

    (function (state) {
        var Runtime = Hyperbars.Runtime;
        var context = state;
        return h('div', {}, [Runtime.each(context['names'], context, function (context, parent, options) {
            return [h('span', {}, [options['@index'], context])]
        })])
    }.bind({}))
    

    在这种情况下 eval() 的性能不是问题,因为您只需要解释生成的字符串一次,然后多次重复使用可执行输出 .

    如果你好奇here,你可以看到代码生成是如何实现的 .

  • 26

    评估并不总是邪恶的 . 有时候它非常合适 .

    然而,eval目前和历史上大量过度使用,他们不知道自己在做什么 . 不幸的是,这包括编写JavaScript教程的人,在某些情况下,这确实会带来安全后果 - 或者更常见的是简单的错误 . 因此,我们越多可以在eval上抛出问号,就越好 . 每当你使用eval时,你需要理智地检查你正在做什么,因为你可能会做得更好,更安全,更清洁 .

    举一个非常典型的例子,设置一个id存储在变量'potato'中的元素的颜色:

    eval('document.' + potato + '.style.color = "red"');
    

    如果上述代码类型的作者对JavaScript对象的工作原理有了线索,他们就会意识到可以使用方括号而不是文字点名,从而避免了对eval的需求:

    document[potato].style.color = 'red';
    

    ...这更容易阅读,也不那么潜在的错误 .

    (但后来,某人/他/他们真正知道他们在做什么会说:

    document.getElementById(potato).style.color = 'red';
    

    这比直接从文档对象访问DOM元素的狡猾的旧技巧更可靠 . )

  • 3

    除非您让eval()成为动态内容(通过cgi或输入),否则它与您页面中的所有其他JavaScript一样安全可靠 .

  • 337

    如果您传递eval用户输入,通常只会出现问题 .

  • 11

    除了其他答案之外,我认为eval语句不能具有高级最小化 .

相关问题