为什么使用 JavaScript eval 函数是一个坏主意?
eval 函数是动态生成代码的一种强大而简单的方法,那么有什么注意事项呢?
The eval function is a powerful and easy way to dynamically generate code, so what are the caveats?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(25)
这是一篇谈论 eval 以及它为何不是邪恶的好文章:
http://www.nczonline.net/博客/2013/06/25/eval-isnt-evil-just-misunderstood/
This is one of good articles talking about eval and how it is not an evil:
http://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/
JavaScript 引擎在编译阶段执行了许多性能优化。 其中一些归结为能够在词法分析时对代码进行本质上的静态分析,并预先确定所有变量和函数声明的位置,以便在执行期间解析标识符所需的工作量更少。
但是,如果引擎在代码中发现 eval(..),它本质上必须假设其对标识符位置的所有感知可能都是无效的,因为它无法在词法分析时准确知道您可以传递给 eval(..) 的代码修改词法范围,或者可以传递给 with 的对象的内容,以创建要查阅的新词法范围。
换句话说,从悲观的角度来看,如果 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
The JavaScript Engine has a number of performance optimizations that it performs during the compilation phase. Some of these boil down to being able to essentially statically analyze the code as it lexes, and pre-determine where all the variable and function declarations are, so that it takes less effort to resolve identifiers during execution.
But if the Engine finds an eval(..) in the code, it essentially has to assume that all its awareness of identifier location may be invalid, because it cannot know at lexing time exactly what code you may pass to eval(..) to modify the lexical scope, or the contents of the object you may pass to with to create a new lexical scope to be consulted.
In other words, in the pessimistic sense, most of those optimizations it would make are pointless if eval(..) is present, so it simply doesn't perform the optimizations at all.
This explains it all.
Reference :
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
这并不总是一个坏主意。 以代码生成为例。 我最近编写了一个名为 Hyperbars 的库,它弥补了 virtual-dom 和 车把。 它通过解析车把模板并将其转换为 hyperscript 来实现此目的,随后由 virtual-dom 使用。 超脚本首先作为字符串生成,在返回它之前,使用
eval()
将其转换为可执行代码。 我发现在这种特殊情况下,eval()
与邪恶完全相反。基本上,在这种情况下
,
eval()
的性能不是问题,因为您只需解释生成的字符串一次,然后多次重复使用可执行输出。如果您好奇的话,您可以看到代码生成是如何实现的 在这里。
It's not always a bad idea. Take for example, code generation. I recently wrote a library called Hyperbars which bridges the gap between virtual-dom and handlebars. It does this by parsing a handlebars template and converting it to hyperscript which is subsequently used by virtual-dom. The hyperscript is generated as a string first and before returning it,
eval()
it to turn it into executable code. I have foundeval()
in this particular situation the exact opposite of evil.Basically from
To this
The performance of
eval()
isn't an issue in a situation like this because you only need to interpret the generated string once and then reuse the executable output many times over.You can see how the code generation was achieved if you're curious here.
我想说的是,如果您在浏览器中运行的 javascript 中使用
eval()
并不重要。*(警告)所有现代浏览器都有一个开发人员控制台,您可以在其中无论如何执行任意 javascript,任何半聪明的开发人员都可以查看您的 JS 源代码并将其需要的任何部分放入开发控制台中以执行他们希望的操作。
*只要您的服务器端点具有正确的验证和 对用户提供的值进行清理,在客户端 JavaScript 中解析和评估什么内容并不重要。
但是,如果您询问是否适合在 PHP 中使用
eval()
,答案是不,除非您将任何可能会影响的值白名单。传递到您的 eval 语句。I would go as far as to say that it doesn't really matter if you use
eval()
in javascript which is run in browsers.*(caveat)All modern browsers have a developer console where you can execute arbitrary javascript anyway and any semi-smart developer can look at your JS source and put whatever bits of it they need to into the dev console to do what they wish.
*As long as your server endpoints have the correct validation & sanitisation of user supplied values, it should not matter what gets parsed and eval'd in your client side javascript.
If you were to ask if it's suitable to use
eval()
in PHP however, the answer is NO, unless you whitelist any values which may be passed to your eval statement.编辑:正如Benjie的评论所暗示的那样,在chrome v108中似乎不再是这种情况,chrome现在似乎可以处理评估脚本的垃圾收集。
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
垃圾收集
浏览器垃圾收集不知道所评估的代码是否可以从内存中删除,因此它只是将其存储起来,直到重新加载页面。
如果您的用户只短暂访问您的页面,这还不算太糟糕,但这对于网络应用程序来说可能是一个问题。
这是一个演示问题的脚本
https://jsfiddle.net/CynderRnAsh/qux1osnw/
如下很简单,因为上面的代码会导致存储少量内存,直到应用程序终止。
当评估的脚本是一个巨大的函数并且按时间间隔调用时,情况会更糟。
EDIT: As Benjie's comment suggests, this no longer seems to be the case in chrome v108, it would seem that chrome can now handle garbage collection of evaled scripts.
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
Garbage collection
The browsers garbage collection has no idea if the code that's eval'ed can be removed from memory so it just keeps it stored until the page is reloaded.
Not too bad if your users are only on your page shortly, but it can be a problem for webapp's.
Here's a script to demo the problem
https://jsfiddle.net/CynderRnAsh/qux1osnw/
Something as simple as the above code causes a small amount of memory to be store until the app dies.
This is worse when the evaled script is a giant function, and called on interval.
eval()
计算或执行参数。从字符串执行 JavaScript 是一个很大的安全风险,使用带有恶意代码的 eval() 可以在未经许可的情况下在您的应用程序中运行。
如果您希望用户输入一些逻辑函数并评估 AND 或 OR,那么 JavaScript eval 函数是完美的。
我可以接受两个字符串和
eval(uate) string1 === string2
等。eval()
evaluates or executes an argument.Executing JavaScript from a string is an BIG security risk, using eval() with malicious code can run inside your application without permission.
If you want the user to input some logical functions and evaluate for AND the OR then the JavaScript eval function is perfect.
I can accept two strings and
eval(uate) string1 === string2
, etc.我不会试图反驳之前所说的任何内容,但我将提供 eval() 的这种用法(据我所知)无法通过任何其他方式完成。 可能还有其他方法来编码,也可能有其他方法来优化它,但这是手动完成的,没有任何附加功能,为了清楚起见,说明了 eval 的使用,实际上没有任何其他选择。 即:动态(或更准确地说)以编程方式创建的对象名称(而不是值)。
编辑:顺便说一句,我不建议(出于迄今为止指出的所有安全原因)您将对象名称基于用户输入。 但我无法想象你有什么充分的理由想要这样做。 不过,我想我会指出这不是一个好主意:)
I won't attempt to refute anything said heretofore, but i will offer this use of eval() that (as far as I know) can't be done any other way. There's probably other ways to code this, and probably ways to optimize it, but this is done longhand and without any bells and whistles for clarity sake to illustrate a use of eval that really doesn't have any other alternatives. That is: dynamical (or more accurately) programmically-created object names (as opposed to values).
EDIT: by the way, I wouldn't suggest (for all the security reasons pointed out heretofore) that you base you object names on user input. I can't imagine any good reason you'd want to do that though. Still, thought I'd point it out that it wouldn't be a good idea :)
随着下一代浏览器推出某种 JavaScript 编译器,这可能会成为一个更大的问题。 在这些较新的浏览器上,通过 Eval 执行的代码的执行效果可能不如 JavaScript 的其余部分。 应该有人做一些分析。
This may become more of an issue as the next generation of browsers come out with some flavor of a JavaScript compiler. Code executed via Eval may not perform as well as the rest of your JavaScript against these newer browsers. Someone should do some profiling.
除了执行用户提交的代码时可能出现的安全问题之外,大多数时候有一种更好的方法,不需要在每次执行时重新解析代码。 匿名函数或对象属性可以取代 eval 的大部分用途,并且更安全、更快速。
Besides the possible security issues if you are executing user-submitted code, most of the time there's a better way that doesn't involve re-parsing the code every time it's executed. Anonymous functions or object properties can replace most uses of eval and are much safer and faster.
它大大降低了您对安全性的信心。
It greatly reduces your level of confidence about security.
eval() 非常强大,可以用来执行 JS 语句或计算表达式。 但问题不在于 eval() 的使用,而只是说明使用 eval() 运行的字符串如何受到恶意方的影响。 最后你将运行恶意代码。 权力越大,责任越大。 因此,如果您正在使用它,请明智地使用它。
这与 eval() 函数关系不大,但这篇文章提供了很好的信息:
http://blogs.popart.com/2009/07/javascript-injection-攻击/
如果您正在寻找 eval() 的基础知识,请看这里:
https://developer.mozilla.org/en-US /docs/Web/JavaScript/Reference/Global_Objects/eval
eval() is very powerful and can be used to execute a JS statement or evaluate an expression. But the question isn't about the uses of eval() but lets just say some how the string you running with eval() is affected by a malicious party. At the end you will be running malicious code. With power comes great responsibility. So use it wisely is you are using it.
This isn't related much to eval() function but this article has pretty good information:
http://blogs.popart.com/2009/07/javascript-injection-attacks/
If you are looking for the basics of eval() look here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
如果您在代码中发现使用了 eval(),请记住这句口头禅“eval() 是邪恶的”。
这
函数接受任意字符串并将其作为 JavaScript 代码执行。 当代码在
问题是预先知道的(不是在运行时确定的),没有理由使用
评估()。
如果代码是在运行时动态生成的,通常有更好的方法
无需 eval() 即可实现目标。
例如,仅使用方括号表示法
访问动态属性更好更简单:
使用
eval()
也有安全隐患,因为您可能正在执行代码(例如来自网络的示例)已被篡改。
这是处理 Ajax 请求的 JSON 响应时的常见反模式。
在那些情况下
最好使用浏览器的内置方法来解析 JSON 响应,以便
确保它是安全有效的。 对于本身不支持
JSON.parse()
的浏览器,您可以使用 JSON.org 中的库。
同样重要的是要记住将字符串传递给
setInterval()
、setTimeout()
、并且
Function()
构造函数在很大程度上与使用eval()
类似,因此应该避免。
在幕后,JavaScript 仍然需要计算和执行
作为编程代码传递的字符串:
使用 new Function() 构造函数与 eval() 类似,应该使用
小心。 它可能是一个强大的结构,但经常被滥用。
如果你绝对必须
使用
eval()
,你可以考虑使用new Function()代替。有一个小潜力
好处是因为在 new Function() 中计算的代码将在本地函数中运行
范围,因此在正在评估的代码中使用 var 定义的任何变量都不会成为
自动全局变量。
防止自动全局变量的另一种方法是包装
eval()
调用立即函数。If you spot the use of eval() in your code, remember the mantra “eval() is evil.”
This
function takes an arbitrary string and executes it as JavaScript code. When the code in
question is known beforehand (not determined at runtime), there’s no reason to use
eval().
If the code is dynamically generated at runtime, there’s often a better way to
achieve the goal without eval().
For example, just using square bracket notation to
access dynamic properties is better and simpler:
Using
eval()
also has security implications, because you might be executing code (forexample coming from the network) that has been tampered with.
This is a common antipattern when dealing with a JSON response from an Ajax request.
In those cases
it’s better to use the browsers’ built-in methods to parse the JSON response to make
sure it’s safe and valid. For browsers that don’t support
JSON.parse()
natively, you canuse a library from JSON.org.
It’s also important to remember that passing strings to
setInterval()
,setTimeout()
,and the
Function()
constructor is, for the most part, similar to usingeval()
and thereforeshould be avoided.
Behind the scenes, JavaScript still has to evaluate and execute
the string you pass as programming code:
Using the new Function() constructor is similar to eval() and should be approached
with care. It could be a powerful construct but is often misused.
If you absolutely must
use
eval()
, you can consider using new Function() instead.There is a small potential
benefit because the code evaluated in new Function() will be running in a local function
scope, so any variables defined with var in the code being evaluated will not become
globals automatically.
Another way to prevent automatic globals is to wrap the
eval()
call into an immediate function.如果您知道在什么上下文中使用它,这不一定那么糟糕。
如果您的应用程序使用
eval()
从某个从 XMLHttpRequest 到您自己的站点,由您可信的服务器端代码创建,这可能不是问题。不受信任的客户端 JavaScript 代码无论如何也做不到那么多。 只要您执行
eval()
的内容来自合理的来源,就可以了。It's not necessarily that bad provided you know what context you're using it in.
If your application is using
eval()
to create an object from some JSON which has come back from an XMLHttpRequest to your own site, created by your trusted server-side code, it's probably not a problem.Untrusted client-side JavaScript code can't do that much anyway. Provided the thing you're executing
eval()
on has come from a reasonable source, you're fine.除非您 100% 确定正在评估的代码来自受信任的来源(通常是您自己的应用程序),否则这肯定会让您的系统遭受跨站点脚本攻击。
Unless you are 100% sure that the code being evaluated is from a trusted source (usually your own application) then it's a surefire way of exposing your system to a cross-site scripting attack.
这是一个可能的安全风险,它具有不同的执行范围,并且效率相当低,因为它为代码的执行创建了一个全新的脚本环境。 请参阅此处了解更多信息:eval。
不过,它非常有用,适度使用可以添加很多好的功能。
It is a possible security risk, it has a different scope of execution, and is quite inefficient, as it creates an entirely new scripting environment for the execution of the code. See here for some more info: eval.
It is quite useful, though, and used with moderation can add a lot of good functionality.
与其他答案一起,我认为 eval 语句不能进行高级最小化。
Along with the rest of the answers, I don't think eval statements can have advanced minimization.
除非您让 eval() 成为动态内容(通过 cgi 或输入),否则它与页面中的所有其他 JavaScript 一样安全可靠。
Unless you let eval() a dynamic content (through cgi or input), it is as safe and solid as all other JavaScript in your page.
需要记住的一件事是,您经常可以使用 eval() 在其他受限制的环境中执行代码 - 阻止特定 JavaScript 函数的社交网站有时可能会通过将它们分解为 eval 块来欺骗它们 -
因此,如果您希望运行一些 JavaScript 代码,否则可能不允许(Myspace,我正在查看你...) 那么 eval() 可能是一个有用的技巧。
然而,由于上述所有原因,您不应该将它用于您自己的代码,因为您可以完全控制它 - 只是没有必要,最好将其归入“棘手的 JavaScript 黑客”架子。
One thing to keep in mind is that you can often use eval() to execute code in an otherwise restricted environment - social networking sites that block specific JavaScript functions can sometimes be fooled by breaking them up in an eval block -
So if you're looking to run some JavaScript code where it might not otherwise be allowed (Myspace, I'm looking at you...) then eval() can be a useful trick.
However, for all the reasons mentioned above, you shouldn't use it for your own code, where you have complete control - it's just not necessary, and better-off relegated to the 'tricky JavaScript hacks' shelf.
我想到两点:
安全性(但只要您自己生成要评估的字符串,这可能不是问题)
性能:在要执行的代码未知之前,无法对其进行优化。 (关于 javascript 和性能,当然Steve Yegge 的演讲)
Two points come to mind:
Security (but as long as you generate the string to be evaluated yourself, this might be a non-issue)
Performance: until the code to be executed is unknown, it cannot be optimized. (about javascript and performance, certainly Steve Yegge's presentation)
如果您传递 eval 用户输入,这通常只是一个问题。
It's generally only an issue if you're passing eval user input.
我相信这是因为它可以从字符串执行任何 JavaScript 函数。 使用它可以让人们更容易地将恶意代码注入到应用程序中。
I believe it's because it can execute any JavaScript function from a string. Using it makes it easier for people to inject rogue code into the application.
将用户输入传递给 eval() 存在安全风险,而且每次调用 eval() 都会创建一个新的 JavaScript 解释器实例。 这可能会占用大量资源。
Passing user input to eval() is a security risk, but also each invocation of eval() creates a new instance of the JavaScript interpreter. This can be a resource hog.
主要是维护和调试更加困难。 这就像一个
goto
。 您可以使用它,但它会使发现问题变得更加困难,并且对于以后可能需要进行更改的人来说也更加困难。Mainly, it's a lot harder to maintain and debug. It's like a
goto
. You can use it, but it makes it harder to find problems and harder on the people who may need to make changes later.eval 并不总是邪恶的。 有时它是完全合适的。
然而,eval 目前和历史上都被那些不知道自己在做什么的人大量过度使用。 不幸的是,这包括编写 JavaScript 教程的人,在某些情况下,这确实会产生安全后果 - 或者更常见的是简单的错误。 因此,我们对 eval 的问号越多越好。 任何时候你使用 eval 时,你都需要对你正在做的事情进行健全性检查,因为你很可能可以用更好、更安全、更干净的方式来做。
举一个非常典型的例子,用存储在变量“potato”中的 id 设置元素的颜色:
如果上述代码的作者了解 JavaScript 对象如何工作的基础知识,他们就会我已经意识到可以使用方括号代替字面点名,从而消除了对 eval:
...的需要,这更容易阅读,并且潜在的错误也更少。
(但是,真正知道自己在做什么的人会说:
这比直接从文档对象访问 DOM 元素的狡猾老把戏更可靠。)
eval isn't always evil. There are times where it's perfectly appropriate.
However, eval is currently and historically massively over-used by people who don't know what they're doing. That includes people writing JavaScript tutorials, unfortunately, and in some cases this can indeed have security consequences - or, more often, simple bugs. So the more we can do to throw a question mark over eval, the better. Any time you use eval you need to sanity-check what you're doing, because chances are you could be doing it a better, safer, cleaner way.
To give an all-too-typical example, to set the colour of an element with an id stored in the variable 'potato':
If the authors of the kind of code above had a clue about the basics of how JavaScript objects work, they'd have realised that square brackets can be used instead of literal dot-names, obviating the need for eval:
...which is much easier to read as well as less potentially buggy.
(But then, someone who /really/ knew what they were doing would say:
which is more reliable than the dodgy old trick of accessing DOM elements straight out of the document object.)
不当使用eval会打开你的
注入攻击代码
调试可能更具挑战性
(没有行号等)
评估代码执行速度较慢(没有机会编译/缓存评估代码)
编辑:正如 @Jeff Walden 在评论中指出的那样,#3 与 2008 年相比,今天的情况不太正确。但是,虽然可能会发生一些已编译脚本的缓存,但这仅限于未经修改而重复评估的脚本。 更可能的情况是您正在评估每次都经过轻微修改的脚本,因此无法缓存。 我们只是说某些评估代码执行速度更慢。
Improper use of eval opens up your
code for injection attacks
Debugging can be more challenging
(no line numbers, etc.)
eval'd code executes slower (no opportunity to compile/cache eval'd code)
Edit: As @Jeff Walden points out in comments, #3 is less true today than it was in 2008. However, while some caching of compiled scripts may happen this will only be limited to scripts that are eval'd repeated with no modification. A more likely scenario is that you are eval'ing scripts that have undergone slight modification each time and as such could not be cached. Let's just say that SOME eval'd code executes more slowly.