关于安全性:为什么使用JavaScript eval函数是一个坏主意?

Why is using the JavaScript eval function a bad idea?

eval函数是动态生成代码的一种强大而简单的方法,那么需要注意的是什么?


  • 不正确使用eval会打开注入攻击代码

  • 调试可能更具挑战性(无行号等)

  • eval'd代码执行速度较慢(没有机会编译/缓存eval'd代码)

  • 编辑:正如@jeff walden在评论中指出的那样,3今天比2008年更不真实。但是,虽然可能会发生编译脚本的某些缓存,但这将仅限于eval不做任何修改而重复的脚本。更可能的情况是,您正在评估的脚本每次都进行了轻微的修改,因此无法缓存。让我们假设一些eval'd代码执行得更慢。


    埃瓦尔并不总是邪恶的。有时它是完全合适的。

    然而,目前和历史上,eval被不知道自己在做什么的人过度使用。不幸的是,这包括编写JavaScript教程的人员,在某些情况下,这确实会带来安全后果——或者更常见的是,简单的错误。因此,我们越能在eval上面加上问号,效果越好。任何时候你使用疏散车,你都需要保持清醒,检查你在做什么,因为很有可能你会用更好、更安全、更清洁的方式。

    为了给出一个非常典型的例子,设置一个ID存储在变量"potato"中的元素的颜色:

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

    如果上面这类代码的作者对javascript对象的工作原理有了一些线索,他们就会意识到可以使用方括号而不是文字点名,这样就不需要进行eval了:

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

    …更容易阅读,也不太可能有问题。

    (但是,如果有人/真的/知道他们在做什么,他会说:

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

    这比直接从文档对象访问dom元素这一老掉牙的技巧更可靠。)


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


    我想到两点:

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

  • 性能:在要执行的代码未知之前,无法对其进行优化。(关于javascript和性能,当然是Steve Yegge的演示)


  • 将用户输入传递给eval()是一个安全风险,但每次调用eval()都会创建一个新的javascript解释器实例。这可能是一种资源占用。


    一般来说,只有通过eval用户输入时才会出现问题。


    主要是,维护和调试要困难得多。它就像一个goto。你可以使用它,但是它会让你更难发现问题,也会让那些以后需要改变的人更难找到问题。


    要记住的一点是,您可以经常使用eval()在其他受限的环境中执行代码-有时在eval块中分解特定的javascript函数会愚弄那些阻止特定javascript函数的社交网站。-

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

    所以,如果你想运行一些原本不允许的javascript代码(myspace,我在看你…),那么eval()是一个很有用的技巧。

    但是,由于上述所有原因,您不应该将其用于您自己的代码,因为您完全可以控制这些代码——这是不必要的,最好将其放在"棘手的javascript黑客"工具架上。


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


    除了其他答案,我不认为eval语句可以高级最小化。


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

    但是,它非常有用,并且与适度一起使用可以添加很多好的功能。


    除非您100%确信正在评估的代码来自可信的源(通常是您自己的应用程序),否则这是一种将系统暴露于跨站点脚本攻击的可靠方法。


    如果你知道在什么环境中使用它,就不一定那么糟糕。

    如果您的应用程序正在使用eval()从某个JSON创建一个对象,该JSON已从xmlhttpRequest返回到您自己的站点,由受信任的服务器端代码创建,那么这可能不是问题。

    不受信任的客户端JavaScript代码无论如何也不能做到这一点。如果你正在执行的eval()源于一个合理的来源,你就可以了。


    我知道这个讨论已经过时了,但我真的很喜欢Google的这种方法,我想和其他人分享这种感觉;)

    另一件事是,你越能理解,你就越能理解,最后你只是不相信某件事是好是坏,只是因为有人这么说:)这是一个非常鼓舞人心的视频,它帮助我自己思考更多:)好的实践是好的,但不要盲目地使用它们:)


    如果您希望用户输入一些逻辑函数并对其进行评估,那么javascript eval函数是完美的。我可以接受两个字符串和eval(uate) string1 === string2等。


    它大大降低了你对安全的信心。


    如果在代码中发现eval()的用法,请记住"eval()是邪恶的"这句咒语。

    这个函数接受任意字符串并将其作为JavaScript代码执行。当代码在问题是预先知道的(在运行时不确定),没有理由使用eval()。如果代码是在运行时动态生成的,通常有更好的方法在不使用eval()的情况下实现目标。例如,只需使用方括号符号访问动态属性更好、更简单:

    1
    2
    3
    4
    5
    6
    7
    // 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仍然需要评估和执行作为编程代码传递的字符串:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // antipatterns
    setTimeout("myFunc()", 1000);
    setTimeout("myFunc(1, 2, 3)", 1000);

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

    使用新的函数()构造函数与eval()类似,应该接近它小心点。它可能是一个强大的结构,但经常被误用。如果你必须的话使用eval(),可以考虑改用new函数()。

    潜力很小好处是,在new函数()中计算的代码将在本地函数中运行作用域,因此在被评估的代码中用var定义的任何变量都不会变为全局自动。

    另一种防止自动全局调用的方法是eval()调用立即函数。


    这是一篇很好的文章,讲的是eval,以及它如何不是一种邪恶:http://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misshood/

    I’m not saying you should go run out and start using eval()
    everywhere. In fact, there are very few good use cases for running
    eval() at all. There are definitely concerns with code clarity,
    debugability, and certainly performance that should not be overlooked.
    But you shouldn’t be afraid to use it when you have a case where
    eval() makes sense. Try not using it first, but don’t let anyone scare
    you into thinking your code is more fragile or less secure when eval()
    is used appropriately.


    eval()非常强大,可以用于执行JS语句或计算表达式。但问题不在于eval()的使用,而在于您使用eval()运行的字符串如何受到恶意方的影响。最后,您将运行恶意代码。有了权力,责任就大了。所以明智地使用它就是你在使用它。这与eval()函数没有太多关系,但本文提供了非常好的信息:http://blogs.popart.com/2009/07/javascript-injection-attacks/如果要查找eval()的基础知识,请查看以下内容:https://developer.mozilla.org/en-us/docs/web/javascript/reference/global_objects/eval


    随着下一代浏览器推出某种风格的JavaScript编译器,这可能成为一个更大的问题。通过eval执行的代码在这些新浏览器上的性能可能不如其他的javascript。有人应该做一些分析。


    除了在执行用户提交的代码时可能出现的安全问题之外,大多数情况下还有一种更好的方法,不需要每次执行代码时都重新解析代码。匿名函数或对象属性可以替代eval的大部分用途,并且更安全、更快。


    我想说的是,如果您在浏览器中运行的javascript中使用eval(),这并不重要。*(警告)

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

    *只要您的服务器端点对用户提供的值进行了正确的验证和消毒,那么在客户端JavaScript中解析和评估什么就不重要了。

    但是,如果您要询问是否适合在PHP中使用eval(),答案是否定的,除非您将可能传递给eval语句的任何值白列出来。


    我不会试图反驳之前所说的任何事情,但我会提供eval()的这种用法(据我所知),这种用法不能以任何其他方式进行。可能还有其他的方法来编写代码,也可能有一些方法来优化它,但是为了清晰起见,这是一个很长的过程,而且没有任何提示和口哨,以说明eval的使用实际上没有任何其他的选择。即:动态(或更准确地说)编程创建的对象名(而不是值)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    //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);

    编辑:顺便说一句,我不建议(出于之前指出的所有安全原因)将对象名建立在用户输入的基础上。不过,我想不出有什么好理由让你这么做。不过,我想我会指出这不是个好主意:)


    javascript引擎在编译阶段执行了许多性能优化。其中一些归根结底就是能够在代码执行时对其进行静态分析,并预先确定所有变量和函数声明的位置,这样在执行期间解析标识符的工作就更少了。

    但是,如果引擎在代码中发现一个eval(..),那么它本质上必须假定它对标识符位置的所有感知都可能是无效的,因为它无法在词法转换时准确地知道可以传递给eval(..)以修改词法作用域的代码,或者传递给它的对象的内容,从而创建一个新的要查询的词法作用域。

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

    这就解释了一切。

    参考文献:

    https://github.com/getify/you dont know js/blob/master/scope%20&;%20闭包/ch2.md eval

    https://github.com/getify/you dont know js/blob/master/scope%20&;%20闭包/ch2.md性能


    这并不总是个坏主意。例如,代码生成。我最近写了一个名为超链接的库,它弥合了虚拟DOM和把手之间的鸿沟。它通过解析一个手柄模板并将其转换为随后由虚拟DOM使用的超脚本来实现这一点。超脚本首先作为字符串生成,在返回它之前,eval()将其转换为可执行代码。我发现在这种情况下,以东十一〔0〕与邪恶完全相反。

    基本上来自

    1
    2
    3
        {{#each names}}
            <span>{{this}}</span>
        {{/each}}

    为了这个

    1
    2
    3
    4
    5
    6
    7
    (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()的性能不是问题,因为您只需要解释生成的字符串一次,然后多次重复使用可执行输出。

    如果您好奇的话,可以看到代码生成是如何实现的。