什么时候JavaScript的eval()不是邪恶的?

我正在编写一些JavaScript代码来parsing用户input的函数(对于类似电子表格的function)。 parsing了公式之后,我可以将它转换成JavaScript并在其上运行eval()以产生结果。

但是,如果我可以避免使用eval() ,因为它是邪恶的(而且,无论正确还是错误,我一直认为它在JavaScript中更加邪恶,因为要评估的代码可能会改变由用户)。

那么,什么时候可以使用它?

我想花一点时间来解决你的问题的前提–eval()是“ 邪恶的 ”。 程序devise语言使用的“ 邪恶 ”一词通常意味着“危险的”,或者更确切地说,“能够以简单的命令造成很多伤害”。 那么,什么时候可以使用危险的东西呢? 当你知道危险是什么时,以及当你采取适当的预防措施。

我们来看看使用eval()的危险。 像其他所有事情一样,可能存在许多小的隐患,但是两大风险–eval()被认为是邪恶的原因是性能和代码注入。

  • 性能 – eval()运行解释器/编译器。 如果你的代码被编译,那么这是一个很大的打击,因为你需要在运行时调用一个可能很重的编译器。 然而,JavaScript仍然是一种解释性语言,这意味着在一般情况下调用eval()并不是一个很大的性能影响(但请参阅下面的具体评论)。
  • 代码注入 – eval()可能会在提升的权限下运行一串代码。 例如,以pipe理员身份运行的程序/ root永远不会想要eval()用户input,因为该input可能是“rm -rf / etc / important-file”或者更糟。 同样,浏览器中的JavaScript也没有这个问题,因为程序无论如何都是在用户自己的帐户中运行的。 服务器端JavaScript可能会有这个问题。

关于你的具体情况。 根据我的理解,你自己生成了string,所以假设你小心不要让像“rm -rf something-important”这样的string被生成,那么就没有代码注入的风险(但是请记住,这是非常非常在一般情况下很难保证这一点)。 另外,如果你在浏览器中运行,我相信代码注入是一个相当小的风险。

至于性能,你必须在编码容易性方面加以减轻。 这是我的意见,如果你parsing的公式,你也可以计算结果在parsing,而不是运行另一个parsing器(eval()内的一个)。 但是使用eval()编码可能更容易,性能命中可能不明显。 它看起来像eval()在这种情况下是没有比任何其他可能可以节省您一些时间的function更邪恶。

eval()不是邪恶的。 或者,如果是这样的话,它就像反思,文件/networkingI / O,线程和IPC在其他语言中是“邪恶的”一样是邪恶的。

如果出于您的目的eval()比手动解释更快,或者使您的代码更简单或更清晰,那么您应该使用它。 如果没有,那么你不应该。 就那么简单。

当你信任的来源。

在JSON的情况下,或多或less很难篡改源,因为它来自您控制的Web服务器。 只要JSON本身不包含用户上传的数据,使用eval就没有什么大的缺点。

在所有其他情况下,我会竭尽全力确保用户提供的数据符合我的规则,然后将其提供给eval()。

让我们得到真正的人:

  1. 现在,每个主stream的浏览器都有一个内置的控制台,你可能会被黑客利用,可以用任何值来调用任何函数 – 为什么他们会打扰使用一个评估语句 – 即使他们可以?

  2. 如果需要0.2秒来编译2000行JavaScript,那么如果我评估四行JSON,性能会下降吗?

即使克罗克福德对“eval是邪恶的”的解释也很薄弱。

 "eval is Evil The eval function is the most misused feature of JavaScript. Avoid it" 

正如克罗克福德自己可能会说的:“这种说法倾向于产生不合理的神经症,不要买它。”

了解eval并了解何时可能有用是更重要的。 例如,eval是评估软件生成的服务器响应的明智工具。

BTW:Prototype.js直接调用eval五次(包括evalJSON()和evalResponse())。 jQuery在parseJSON中使用它(通过函数构造函数)。

我倾向于遵循克罗克福德的 eval() 的build议 ,并完全避免它。 即使似乎需要它的方式也不行。 例如, setTimeout()允许你传递一个函数而不是eval。

 setTimeout(function() { alert('hi'); }, 1000); 

即使它是一个值得信赖的源代码,我也不会使用它,因为JSON返回的代码可能会出现乱码,最多只能做一些不可思议的事情,最糟糕的是会暴露一些不好的东西。

Eval是用于模板化代码的编译的补充。 通过模板,我的意思是你写一个简化的模板生成器,生成有用的模板代码,从而提高开发速度。

我写了一个框架,开发人员不使用EVAL,但他们使用我们的框架,反过来框架必须使用EVAL生成模板。

使用以下方法可以提高EVAL的性能; 而不是执行脚本,你必须返回一个函数。

 var a = eval("3 + 5"); 

它应该组织为

 var f = eval("(function(a,b) { return a + b; })"); var a = f(3,5); 

cachingf肯定会提高速度。

另外Chrome允许非常容易地debugging这些function。

关于安全性,使用评估与否不会有什么区别,

  1. 首先,浏览器调用沙箱中的整个脚本。
  2. 在EVAL中任何邪恶的代码在浏览器本身都是邪恶的。 攻击者或任何人都可以轻松地在DOM中注入一个脚本节点,如果他/她能够评估任何东西,就可以做任何事情。 不使用EVAL不会有任何区别。
  3. 大多数服务器端的安全性是有害的。 糟糕的cookievalidation或者在服务器上执行不良的ACL会导致大多数攻击。
  4. 最近的Java漏洞等在Java的本地代码中出现。 JavaScript被devise为在沙箱中运行,而小程序则被devise为在带有证书的沙箱外运行,从而导致漏洞和许多其他事情。
  5. 编写模仿浏览器的代码并不困难。 您只需使用您最喜欢的用户代理string向服务器发送HTTP请求即可。 无论如何,所有testing工具都可以模拟浏览器。 如果攻击者想伤害你,EVAL是他们的最后一招。 他们有很多其他的方式来处理你的服务器端的安全。
  6. 浏览器DOM不能访问文件,也不能访问用户名。 实际上eval可以访问的机器上没有任何东西。

如果您的服务器端安全性足够稳定,任何人都可以从任何地方进行攻击,则不必担心EVAL。 正如我所提到的,如果EVAL不存在,攻击者有许多工具可以侵入你的服务器,而不pipe你的浏览器的EVAL能力如何。

Eval仅适用于生成一些模板,以便根据事先未使用的内容进行复杂的string处理。 比如,我会更喜欢

 "FirstName + ' ' + LastName" 

而不是

 "LastName + ' ' + FirstName" 

作为我的显示名称,它可以来自数据库,而不是硬编码。

当你需要使用eval()时,唯一的例子是当你需要运行dynamicJS时。 我在谈论你从服务器asynchronous下载的JS …

… 10次中的9次你可以很容易地避免重构。

我看到人们主张不使用eval,因为是邪恶的 ,但是我看到同样的人dynamic地使用Function和setTimeout,所以他们引擎盖使用eval:D

顺便说一句,如果你的沙箱不够确定(例如,如果你在一个允许代码注入的网站上工作),eval是你的最后一个问题。 安全的基本规则是所有的input都是邪恶的,但是在JavaScript的情况下, 即使 JavaScript本身也可能是邪恶的,因为在JavaScript中你可以覆盖任何函数,而你不能确定你是否使用真正的函数,如果一个恶意代码在你之前开始,你不能相信任何JavaScript内置函数:D

现在这个post的结尾是:

如果你真的需要它(80%的时间不需要eval),你确定你在做什么,只需使用eval(或更好的函数;)),闭包和OOP覆盖80/90%的在这种情况下,可以使用另一种逻辑来替代eval,剩下的就是dynamic生成的代码(例如,如果你正在编写一个解释器),正如你所说的评估JSON(在这里你可以使用Crockford安全评估)

微软解释了为什么IE浏览器中的浏览器中的eval()速度较慢, IE + JavaScript性能build议第2部分:JavaScript代码效率低下

如果您完全控制传递给eval函数的代码,则可以使用它。

在Chrome(v28.0.1500.72)中进行debugging时,我发现如果variables没有用在产生闭包的嵌套函数中,那么variables不会被绑定到闭包。 我想,这是对JavaScript引擎的优化。

但是 :当在引起闭包的函数内部使用eval()时,即使根本不使用外层函数的所有variables,也会将其绑定到闭包。 如果有人有时间testing是否可以产生内存泄漏,请在下面给我留言。

这是我的testing代码:

 (function () { var eval = function (arg) { }; function evalTest() { var used = "used"; var unused = "not used"; (function () { used.toString(); // Variable "unused" is visible in debugger eval("1"); })(); } evalTest(); })(); (function () { var eval = function (arg) { }; function evalTest() { var used = "used"; var unused = "not used"; (function () { used.toString(); // Variable "unused" is NOT visible in debugger var noval = eval; noval("1"); })(); } evalTest(); })(); (function () { var noval = function (arg) { }; function evalTest() { var used = "used"; var unused = "not used"; (function () { used.toString(); // Variable "unused" is NOT visible in debugger noval("1"); })(); } evalTest(); })(); 

我想指出的是,eval()不一定是指本地的eval()函数。 这一切都取决于function的名称 。 所以当用一个别名(比如说var noval = eval;然后在一个内部函数noval(expression); )中调用本地eval()时,那么noval(expression);的求值可能会失败,当它引用应该是闭包的一部分的variables时,但实际上不是。

eval很less是正确的select。 虽然可能有很多情况下,您可以通过将脚本连接在一起并实时运行来完成所需的工作,但通常您可以使用更强大和可维护的技术:关联数组表示法( obj["prop"]obj.prop相同),闭包,面向对象的技巧,function性技巧 – 用它们代替。

我的看法是,eval对于客户端的web应用程序来说是一个非常强大的function,并且安全…和JavaScript一样安全,不是。 :-)安全问题基本上是一个服务器端问题,因为现在,像Firebug这样的工具,你可以攻击任何JavaScript应用程序。

就客户端脚本而言,我认为安全问题是一个有争议的问题。 所有加载到浏览器中的东西都会受到操纵,应该这样处理。 当有更简单的方法来执行JavaScript代码和/或操作DOM中的对象(如浏览器中的URL栏)时,使用eval()语句的风险是零。

 javascript:alert("hello"); 

如果有人想操纵他们的DOM,我说摇摆。 安全防止任何types的攻击应始终是服务器应用程序的责任期。

从实用的angular度来看,在可以做其他事情的情况下使用eval()是没有好处的。 但是,在某些情况下应该使用eval。 如果是这样的话,这绝对是可以做到的,没有任何冒险的风险。

 <html> <body> <textarea id="output"></textarea><br/> <input type="text" id="input" /> <button id="button" onclick="execute()">eval</button> <script type="text/javascript"> var execute = function(){ var inputEl = document.getElementById('input'); var toEval = inputEl.value; var outputEl = document.getElementById('output'); var output = ""; try { output = eval(toEval); } catch(err){ for(var key in err){ output += key + ": " + err[key] + "\r\n"; } } outputEl.value = output; } </script> <body> </html> 

什么时候JavaScript的eval()不是邪恶的?

我总是试图阻止使用eval 。 几乎总是有一个更清洁和可维护的解决scheme。 即使对于JSONparsing,也不需要 Eval。 Eval 增加了维护地狱 。 道格拉斯·克罗克福德(Douglas Crockford)这样的大师们并不是没有理由的。

但是我发现了一个应该使用它的例子:

当你需要通过expression。

例如,我有一个函数为我构build了一个通用的google.maps.ImageMapType对象,但是我需要告诉它这个配方,它应该如何从zoomcoord参数中构造tile URL:

 my_func({ name: "OSM", tileURLexpr: '"http://tile.openstreetmap.org/"+b+"/"+a.x+"/"+a.y+".png"', ... }); function my_func(opts) { return new google.maps.ImageMapType({ getTileUrl: function (coord, zoom) { var b = zoom; var a = coord; return eval(opts.tileURLexpr); }, .... }); } 

我使用evalimport的例子。

通常如何做

 var components = require('components'); var Button = components.Button; var ComboBox = components.ComboBox; var CheckBox = components.CheckBox; ... // That quickly gets very boring 

但是在eval和一个小帮手函数的帮助下,它变得更好看了:

 var components = require('components'); eval(importable('components', 'Button', 'ComboBox', 'CheckBox', ...)); 

importable可能看起来像(这个版本不支持导入具体成员)。

 function importable(path) { var name; var pkg = eval(path); var result = '\n'; for (name in pkg) { result += 'if (name !== undefined) throw "import error: name already exists";\n'.replace(/name/g, name); } for (name in pkg) { result += 'var name = path.name;\n'.replace(/name/g, name).replace('path', path); } return result; } 

只有在testing时,如果可能的话。 另外请注意,eval()比其他专门的JSON等评估者慢得多。

代码生成。 我最近写了一个名为Hyperbars的图书馆,它填补了virtual-dom和handlebars之间的空白。 它通过parsing一个句柄模板并将其转换为hyperscript来完成 。 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()函数中的内容,但这不是一个安全问题,因为他可以操纵网站的源代码,因此可以自行更改JavaScript代码。

那么…什么时候不使用eval()? 只有当第三方有可能改变它时,Eval()才应该被使用。 就像拦截客户端和服务器之间的连接一样(但如果这是一个问题,请使用HTTPS)。 你不应该eval()来parsing其他人在论坛中写的代码。

如果真的需要eval不是邪恶的。 但是我偶然发现的eval的用法中有99.9%是不需要的(不包括setTimeout的东西)。

对我来说,邪恶不是一个performance甚至是一个安全问题(好吧,间接就是两者)。 eval所有这些不必要的用途都会join维护地狱。 重构工具被抛弃。 search代码很难。 这些事件的意想不到的影响是军团。

当你没有macros时,Eval对代码生成很有用。

对于一个愚蠢的例子,如果你正在编写一个Brainfuck编译器,你可能需要构造一个函数来执行指令序列作为一个string,并对其进行评估以返回一个函数。

当使用parsing函数(例如,jQuery.parseJSON)parsingJSON结构时,它需要JSON文件的完美结构(每个属性名称都使用双引号)。 但是,JavaScript更加灵活。 因此,你可以使用eval()来避免它。