When is JavaScript’s eval() not evil?


When is JavaScript’s eval() not evil?

I’m writing some JavaScript code to parse user-entered functions (for spreadsheet-like functionality). Having parsed the formula I could convert it into JavaScript and run eval() on it to yield the result.
However, I’ve always shied away from using eval() if I can avoid it because it’s evil (and, rightly or wrongly, I’ve always thought it is even more evil in JavaScript, because the code to be evaluated might be changed by the user).
So, when it is OK to use it?


Solution 1:

I’d like to take a moment to address the premise of your question – that eval() is “evil“. The word “evil“, as used by programming language people, usually means “dangerous”, or more precisely “able to cause lots of harm with a simple-looking command”. So, when is it OK to use something dangerous? When you know what the danger is, and when you’re taking the appropriate precautions.

To the point, let’s look at the dangers in the use of eval(). There are probably many small hidden dangers just like everything else, but the two big risks – the reason why eval() is considered evil – are performance and code injection.

  • Performance – eval() runs the interpreter/compiler. If your code is compiled, then this is a big hit, because you need to call a possibly-heavy compiler in the middle of run-time. However, JavaScript is still mostly an interpreted language, which means that calling eval() is not a big performance hit in the general case (but see my specific remarks below).
  • Code injection – eval() potentially runs a string of code under elevated privileges. For example, a program running as administrator/root would never want to eval() user input, because that input could potentially be “rm -rf /etc/important-file” or worse. Again, JavaScript in a browser doesn’t have that problem, because the program is running in the user’s own account anyway. Server-side JavaScript could have that problem.

On to your specific case. From what I understand, you’re generating the strings yourself, so assuming you’re careful not to allow a string like “rm -rf something-important” to be generated, there’s no code injection risk (but please remember, it’s very very hard to ensure this in the general case). Also, if you’re running in the browser then code injection is a pretty minor risk, I believe.

As for performance, you’ll have to weight that against ease of coding. It is my opinion that if you’re parsing the formula, you might as well compute the result during the parse rather than run another parser (the one inside eval()). But it may be easier to code using eval(), and the performance hit will probably be unnoticeable. It looks like eval() in this case is no more evil than any other function that could possibly save you some time.

Solution 2:

eval() isn’t evil. Or, if it is, it’s evil in the same way that reflection, file/network I/O, threading, and IPC are “evil” in other languages.

If, for your purpose, eval() is faster than manual interpretation, or makes your code simpler, or more clear… then you should use it. If neither, then you shouldn’t. Simple as that.

Solution 3:

When you trust the source.

In case of JSON, it is more or less hard to tamper with the source, because it comes from a web server you control. As long as the JSON itself contains no data a user has uploaded, there is no major drawback to use eval.

In all other cases I would go great lengths to ensure user supplied data conforms to my rules before feeding it to eval().

Solution 4:

Let’s get real folks:

  1. Every major browser now has a built-in console which your would-be hacker can use with abundance to invoke any function with any value – why would they bother to use an eval statement – even if they could?

  2. If it takes 0.2 seconds to compile 2000 lines of JavaScript, what is my performance degradation if I eval four lines of JSON?

Even Crockford’s explanation for ‘eval is evil’ is weak.

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

As Crockford himself might say “This kind of statement tends to generate irrational neurosis. Don’t buy it.”

Understanding eval and knowing when it might be useful is way more important. For example, eval is a sensible tool for evaluating server responses that were generated by your software.

BTW: Prototype.js calls eval directly five times (including in evalJSON() and evalResponse()). jQuery uses it in parseJSON (via Function constructor).

Solution 5:

I tend to follow Crockford’s advice for eval(), and avoid it altogether. Even ways that appear to require it do not. For example, the setTimeout() allows you to pass a function rather than eval.

setTimeout(function() {
}, 1000);

Even if it’s a trusted source, I don’t use it, because the code returned by JSON might be garbled, which could at best do something wonky, at worst, expose something bad.

Solution 6:

Eval is complementary to compilation which is used in templating the code. By templating I mean that you write a simplified template generator that generates useful template code which increases development speed.

I have written a framework, where developers don’t use EVAL, but they use our framework and in turn that framework has to use EVAL to generate templates.

Performance of EVAL can be increased by using the following method; instead of executing the script, you must return a function.

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

It should be organized as

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

var a = f(3,5);

Caching f will certainly improve the speed.

Also Chrome allows debugging of such functions very easily.

Regarding security, using eval or not will hardly make any difference,

  1. First of all, the browser invokes the entire script in a sandbox.
  2. Any code that is evil in EVAL, is evil in the browser itself. The attacker or anyone can easily inject a script node in DOM and do anything if he/she can eval anything. Not using EVAL will not make any difference.
  3. It is mostly poor server-side security that is harmful. Poor cookies validation or poor ACL implementation on the server causes most attacks.
  4. A recent Java vulnerability, etc. was there in Java’s native code. JavaScript was and is designed to run in a sandbox, whereas applets were designed to run outside a sandbox with certificates, etc. that lead to vulnerabilities and many other things.
  5. Writing code for imitating a browser is not difficult. All you have to do is make a HTTP request to the server with your favourite user agent string. All testing tools mock browsers anyway; if an attacker want to harm you, EVAL is their last resort. They have many other ways to deal with your server-side security.
  6. The browser DOM does not have access to files and not a user name. In fact nothing on the machine that eval can give access to.

If your server-side security is solid enough for anyone to attack from anywhere, you should not worry about EVAL. As I mentioned, if EVAL would not exist, attackers have many tools to hack into your server irrespective of your browser’s EVAL capability.

Eval is only good for generating some templates to do complex string processing based on something that is not used in advance. For example, I will prefer

"FirstName + ' ' + LastName"

As opposed to

"LastName + ' ' + FirstName"

As my display name, which can come from a database and which is not hardcoded.