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

586

eval函数是一种强大且易于动态生成代码的方法,那么有哪些需要注意的地方呢?


100
不要使用eval()函数 by Simon Willison - http://24ways.org/2005/dont-be-eval - Brian Singh
6
如http://moduscreate.com/javascript-performance-tips-tricks/中所概述的- (new Function(str))()比eval(str)更具性能。这只是我的个人观点 :) - Grgur
3
显然,Chrome浏览器上的新功能(a)比eval(a)慢67%。 - what is sleep
3
我添加了一个静态函数,只是为了比较性能。http://jsperf.com/eval-vs-new-function/2 - Nepoxx
1
@Nepoxx,你的网站挂了。 - 9pfs
显示剩余2条评论
25个回答

415
  1. 不当地使用eval会使您的代码容易受到注入攻击。

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

  3. 使用eval执行的代码运行速度较慢(无法编译/缓存eval的代码的机会)。

编辑:正如@Jeff Walden在评论中指出的那样,#3比2008年时不那么准确。然而,虽然一些已编译脚本的缓存可能会发生,但这仅限于重复执行且未经修改的脚本。更有可能的情况是,您每次都会eval一些经过轻微修改的脚本,因此无法缓存。让我们只说有些eval'd代码执行较慢。


3
@JeffWalden,很棒的评论。虽然我知道你发表这个评论已经有一年了,但我已经更新了我的帖子。如果你像Jeff那样对你的评论进行了一定的限定,那么我可能会同意你的观点。Jeff指出了关键:“多次评估相同的字符串可以避免解析开销”。就目前而言,你是错的; 在许多情况下,#3是正确的。 - Prestaul
7
@Prestaul:既然假定的攻击者可以使用任何开发工具来更改客户端JavaScript中的代码,为什么您要说Eval()会让您的代码容易受到注入攻击呢?它难道不是已经开放了吗?(当然我是在谈论客户端JavaScript) - Eduardo Molteni
71
@EduardoMolteni,我们无法阻止用户在自己的浏览器中执行JavaScript,也不关心这一点。我们试图避免的攻击是当用户提供的值被保存起来之后,再放入到JavaScript代码中并被eval函数执行的情况。例如,我可能将我的用户名设置为:“badHackerGuy'); doMaliciousThings();”,如果您将我的用户名与某些脚本连接起来,并在其他人的浏览器中执行它,则我可以运行任何我想要的JavaScript代码(例如,强制他们给我的帖子点赞、将他们的数据发布到我的服务器等)。 - Prestaul
3
通常情况下,对于相当多的功能调用来说, #1 是正确的,如果不是大多数的话。eval() 不应该因为经验不足的程序员滥用而被有经验的程序员单独挑选并避免使用。然而,有经验的程序员通常在他们的代码中拥有更好的架构,由于这种更好的架构,eval() 很少被需要甚至被考虑到。 - frodeborli
4
@TamilVendhan 当然可以设置断点。您可以通过在源代码中添加“debugger;”语句来访问Chrome为您的评估代码创建的虚拟文件。这将停止程序在该行上的执行。然后,您可以像处理其他JS文件一样添加调试断点。 - Sid
显示剩余16条评论

353

eval并不总是有害的。有时候使用它是完全合适的。

然而,eval目前和历史上被那些不知道自己在做什么的人大量滥用。这包括撰写JavaScript教程的人,不幸的是,在某些情况下,这确实可能会带来安全后果——或者更常见的是简单的错误。因此,我们越能对eval提出质疑,就越好。每次使用eval都需要对自己所做的事情进行合理性检查,因为很有可能你可以用更好、更安全、更清洁的方式完成。

举一个典型的例子,要设置存储在变量“potato”中的元素的颜色:

eval('document.' + potato + '.style.color = "red"');
如果上面代码的作者对JavaScript对象如何工作的基础知识有所了解,他们就会意识到可以使用方括号代替字面点名称,从而避免使用eval的需要。
document[potato].style.color = 'red';

…这样的写法更易读,同时潜在的漏洞也会减少。

(不过,真正知道该怎么做的人可能会说:

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

这种方法比直接访问文档对象的DOM元素更加可靠。


87
嗯,我猜当我第一次学习 JavaScript 时运气真好。我总是使用“document.getElementById”来访问 DOM;具有讽刺意味的是,当时我之所以这样做只是因为我不知道 JavaScript 中的对象是如何工作的;-) - Mike Spross
5
同意。有时使用 eval 是可以的,例如对于来自 Web 服务的 JSON 响应。 - schoetbi
45
对于 JSON,您应该使用 JSON.parse() 而不是 eval() - alexia
4
@bobince http://code.google.com/p/json-sans-eval/ 可在所有浏览器上运行,https://github.com/douglascrockford/JSON-js 也是如此。Doug Crockford的json2.js 在内部确实使用了 eval,但进行了检查。此外,它与浏览器内置的 JSON 支持具备前向兼容性。 - Martijn
9
有一种叫做特征检测和polyfills的东西,可以处理缺失的JSON库等问题(查看http://modernizr.com/)。 - MauganRa
显示剩余3条评论

39

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


7
那么,有什么替代方案吗? - moderns
5
真正的替代方法就是编写不需要它的代码。Crockford对此进行了详细阐述,如果你需要使用它,他几乎会说这是程序设计缺陷,需要重新设计。实际上,我也同意他的观点。尽管JS存在一些缺陷,但它非常灵活,可以为其留下很多空间来实现灵活性。 - kemiller2002
3
不正确,大多数框架都有解析JSON的方法,如果不使用框架,可以使用JSON.parse()函数。大多数浏览器都支持它,如果你真的很急,你也可以很容易地编写一个JSON解析器。 - kemiller2002
6
我不认同这个论点,因为已经很容易向JavaScript应用程序中注入恶意代码了。我们有浏览器控制台、脚本扩展等等...每一行发送给客户端的代码都是可选的,客户端可以自由选择是否执行它们。 - user2867288
7
重点在于,对我来说更容易向您的浏览器注入代码。 假设您正在对查询字符串使用eval。 如果我诱骗您点击一个链接,该链接带有我的查询字符串,并前往该网站,那么我现在就可以在您的计算机上执行我的代码,获得了浏览器的完全权限。 我想记录下您在该网站上输入的所有内容并将其发送给我? 完成了,并且无法阻止我,因为当eval执行时,浏览器会授予其最高权限。 - kemiller2002
显示剩余6条评论

30

如果您正在传递 eval 用户输入,则通常只会出现问题。


1
这意味着一些简单的页面计算不会对任何事情造成伤害。很好知道这一点。 - Sohan Arafat

28

有两点需要考虑:

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

  2. 性能:在未知要执行的代码之前,无法进行优化。(关于 JavaScript 和性能,可以参考 Steve Yegge 的演示文稿《动态语言反击》)


10
为什么安全性是一个问题,如果客户端无论如何都可以对我们的代码做任何他/她想做的事情?Greasemonkey? - Paweł Brewczynski
5
@PaulBrewczynski,安全问题出现在用户A将他的一部分代码保存为eval,然后,那段小代码片段在用户B的浏览器上运行时。 - Felipe Pereira

23

将用户输入传递给eval()存在安全风险,而且每次调用eval()都会创建一个新的JavaScript解释器实例。这可能会消耗大量资源。


31
自从我回答这个问题已经过去了3年多,我的理解有所加深,可以说是更加深入了。实际发生的是创建了一个新的执行上下文。详见http://dmitrysoshnikov.com/ecmascript/chapter-1-execution-contexts/。 - Andrew Hedges

16

主要是,在维护和调试方面更加困难。就像使用goto语句一样,它可以使用,但这会使问题更难以找到并且对可能需要稍后进行更改的人员更加困难。


Eval可以用来替代缺失的元编程特性,比如模板。我更喜欢紧凑的生成器方式,而不是无休止的函数列表。 - weaknespase
只要字符串不来自用户,或者限制在浏览器中,你就可以使用。JavaScript有很多元编程能力,比如改变原型、obj[成员]、代理、json.parse、window、装饰函数(副词)其中newf = decorator(oldf)、高阶函数如Array.prototype.map(f)、将参数传递给其他函数、通过{}传递关键字参数。你能告诉我一个不能用这些代替eval的用例吗? - aoeu256

14

需要记住的一件事是,你通常可以使用eval()在受限环境中执行代码 - 阻止特定JavaScript函数的社交网络网站有时可以通过在eval块中打破它们来欺骗它们 -

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

如果您想在可能不允许的地方运行一些JavaScript代码(Myspace,我在看你……),那么eval()可能是一个有用的技巧。

然而,出于上述所有原因,您不应该在自己可以完全控制的代码中使用它-这是不必要的,并且最好将其归类为“棘手的JavaScript黑客”货架。


1
只需更新上面的代码.. --hi there!-- 需要加引号,因为它是一个字符串。eval('al' + 'er' + 't(' + '"嗨!你好!"' + ')'); - Mahesh
3
这段代码的意思是:创建一个构造函数对象,然后使用该对象创建另一个对象,并调用该对象的构造函数,弹出一个警告框,显示 "hi there!"。 - Konrad Borowski
5
天啊,有些社交网络网站限制alert()函数但允许eval()函数?! - joshden

13

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


1
如果你的内容不是动态的,那么使用 eval 的理由是什么呢?实际上,你可以将代码放在一个函数中并调用它。 - Periata Breatta
举个例子,解析来自Ajax调用的返回值(类似JSON、服务器定义的字符串等)。 - Thevs
2
哦,我明白了。我会称它们为动态的,因为客户端事先不知道它们是什么,但现在我明白你的意思了。 - Periata Breatta

8
除了其他答案之外,我认为eval语句无法进行高级最小化。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接