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

eval函数是dynamic生成代码的一个强大而简单的方法,那么注意什么呢?

  1. eval的不当使用会打开注入攻击的代码

  2. debugging可能更具挑战性(没有行号等)

  3. eval'd代码执行较慢(没有机会编译/cachingeval'd代码)

编辑:正如@Jeff Walden在评论中指出的那样,现在的#3与2008年相比并不是那么真实。然而,尽pipe可能发生一些编译脚本的caching,但是这只会被限制在没有修改的情况下被重复评估的脚本。 更可能的情况是,您正在评估每次经历轻微修改的脚本,因此无法caching。 让我们只是说,一些eval'd代码执行更慢。

eval并不总是邪恶的。 有时候这是完全合适的。

但是,eval目前和历史上被不知道自己在做什么的人大量使用。 这包括编写JavaScript教程的人,不幸的是,在某些情况下,这确实会带来安全后果 – 或者更常见的是简单的错误。 所以,我们可以做的越多,越好的问题。 任何时候你使用eval,你都需要理智地检查你正在做什么,因为你有可能做的更好,更安全,更清洁。

为了给出一个非常典型的例子,设置一个存储在variables“potato”中的id的元素的颜色:

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

如果上述代码的作者对JavaScript对象的基本工作有了线索,他们会意识到可以使用方括号来代替文字点名,从而避免了对eval的需要:

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

…这是更容易阅读,以及更less的潜在错误。

(但是,那些真正知道自己在做什么的人会说:

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

这比直接从文档对象中访问DOM元素的老式技巧更可靠。)

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

有两点让人想起:

  1. 安全性(但只要你自己生成string来评估,这可能是一个没有问题)

  2. 性能:直到要执行的代码是未知的,它不能被优化。 (关于JavaScript和性能,当然Steve Yegge的介绍 )

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

这通常只是一个问题,如果你传递eval用户input。

主要是维护和debugging要困难得多。 这就像一个goto 。 您可以使用它,但是这会让后来需要进行更改的人难以发现问题和困难。

有一件事要记住,你可以经常使用eval()在一个受限制的环境中执行代码 – 阻止特定JavaScript函数的社交网站有时可能被愚弄在一个eval块中 –

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

所以,如果你想运行一些JavaScript代码( Myspace ,我正在看你…),那么eval()可以是一个有用的技巧。

但是,由于上面提到的所有原因,您不应该将它用于您自己的代码,在那里您可以完全控制 – 这不是必要的,而且更好的是放在“棘手的JavaScript黑客”架子上。

除非让eval()成为dynamic内容(通过CGI或input),否则就像页面中的其他JavaScript一样安全可靠。

除了其余的答案,我不认为评估报告可以有先进的最小化。

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

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

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

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

这是相当有用的,虽然,适度使用可以添加很多很好的function。

这大大降低了您对安全性的信心。

只要你知道你使用的是什么环境,这不一定是坏事。

如果您的应用程序正在使用eval()从某个JSON创build一个对象,这个JSON已经从XMLHttpRequest回到您自己的网站,并由您的可信服务器端代码创build,那么这可能不是问题。

不受信任的客户端JavaScript代码无法做到这一点。 只要你想要的东西来自一个合理的来源,你没事。

如果你想让用户input一些逻辑函数并评估AND,那么JavaScript的eval函数是完美的。 我可以接受两个string和eval(uate) string1 === string2

除了执行用户提交的代码时可能存在的安全问题之外,大多数情况下还有更好的方法,不需要每次执行代码时都要重新parsing代码。 匿名函数或对象属性可以取代eval的大多数用法,并且更安全,更快速。

随着下一代浏览器出现一些JavaScript编译器的风味,这可能会变成更多的问题。 通过Eval执行的代码可能无法像对待这些新的浏览器一样执行JavaScript的其余部分。 有人应该做一些分析。

这是一个很好的文章谈论评估和如何不是邪恶的: http : //www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/

我并不是说你应该跑出去开始使用eval()。 实际上,运行eval()的用例几乎没有。 代码清晰度,可debugging性以及肯定的性能肯定是不容忽视的。 但是当你遇到eval()有意义的情况时,你不应该害怕使用它。 尽量不要使用它,但不要让任何人吓到你认为你的代码更脆弱或不太安全时,适当使用eval()。

JavaScript引擎在编译阶段有很多性能优化。 其中一些归结为能够基本静态地分析代码,并预先确定所有variables和函数声明的位置,以便在执行期间解决标识符的花费较less。

但是,如果引擎在代码中find了一个eval(..),那么它本质上必须假设它对标识符位置的所有意识都可能是无效的,因为它无法准确地知道哪些代码可以传递给eval(..)修改词汇范围,或者可以传递的对象的内容来创build一个新的词汇范围进行咨询。

换句话说,从悲观的angular度来说,如果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

如果您在代码中使用了eval(),请记住“eval()是邪恶的”。

这个函数接受一个任意的string,并将其作为JavaScript代码执行。 当有问题的代码事先知道(不是在运行时确定的),没有理由使用eval()。 如果代码是在运行时dynamic生成的,那么在没有eval()的情况下通常有更好的方法来实现目标。 例如,使用方括号表示法来访问dynamic属性会更好,更简单:

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

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

记住将string传递给setInterval()setTimeout()Function()构造Function()大部分类似于使用eval() ,因此应该避免。

在幕后,JavaScript仍然需要评估和执行你作为编程代码传递的string:

 // 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()。

有一个小的潜在好处,因为在新的函数()中评估的代码将在本地函数范围内运行,所以在被评估代码中用var定义的variables将不会自动变为全局variables。

另一种防止自动全局variables的方法是将eval()调用包装成直接函数。

eval()是非常强大的,可以用来执行JS语句或评估一个expression式。 但问题不在于使用eval(),而只是说一下eval()运行的string是如何受恶意方的影响的。 最后你会运行恶意代码。 有了权力,责任重大。 所以明智地使用你正在使用它。 这与eval()函数无关,但是这篇文章有很好的信息: http : //blogs.popart.com/2009/07/javascript-injection-attacks/如果你正在寻找eval()的基础知识,看这里: https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval

这并不总是一个坏主意。 举例来说,代码生成。 我最近写了一个名为Hyperbars的图书馆,它填补了virtual-dom和handlebars之间的空白。 它通过parsing句柄模板并将其转换为随后由虚拟目录使用的超文本标记来完成 。 hyperscript是作为一个string生成的,在返回之前, 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()的性能不是问题,因为您只需要解释生成的string一次,然后重复使用可执行的输出。

如果你对这里有好奇,你可以看看代码生成是如何实现的。

我不会试图反驳迄今为止所说的任何东西,但是我会提供eval()的使用(据我所知)不能以其他方式完成。 可能还有其他的方法来编码,也可能是优化它的方法,但是为了清楚起见,这是徒劳的,没有任何花里胡哨的工作来说明eval的使用,而这个方法真的没有其他的select。 那就是:dynamic的(或更准确地)程序化创build的对象名称(而不是数值)。

 //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议(因为所有的安全理由指出),你基于你的用户input对象名称。 我无法想象有什么好的理由,但是你想这样做。 不过,以为我会指出,这不会是一个好主意:)