Hi, beginner to web development here working through The Odin Project. I'm about to tackle a calculator project and was given explicit instruction NOT to use the eval() function due to it being dangerous. This piqued my curiosity and so I found out that it can be a security vulnerability since it allows an external user to inject code into the DOM. What I don't seem to understand is - how is this any different than just using the console to do the same thing? Also, if each person who accesses a webpage is served a copy of that page, how can e.g. client1 inject any code that could also affect client2 trying to access the page at a later time?
eval() executes a string of characters as code. You use eval() precisely because the string contents are not known in advance, or even generated server-side; basically, you need eval() because the JavaScript itself will generate the string from data which is available only dynamically, in the client.
Thus, eval() makes sense in situations where the JavaScript code will generate code. This is not intrinsically evil, but it is hard to do securely. Programming languages are designed to allow a human being to write instructions that a computer understands; to that effect, any language is full of small quirks and special behaviours that are supposed to help the human programmer (e.g. the automatic adding of ';' at the end of some statements in JavaScript). This is all nice and dandy for "normal" programming; but when you generate code from another program, based on data which may be potentially hostile (e.g. string excerpt from other site users), then you have to, as the developer for the code generator, know about all these quirks, and prevent hostile data to exploit them in damaging ways.
In that sense, code generators (and thus eval()) incur the same conceptual issues as raw SQL and its consequence, SQL injection attacks. Assembling at runtime an SQL request from externally provided parameters can be done securely, but this requires minding an awful lot of details, so the usual advice is not to do that. This relates to the usual conundrum of security, i.e. that it is not testable: you can test whether some piece of code works properly on correct data, but not that it never works improperly on incorrect data. Similarly, using eval() securely is possible, but it is so hard in practice that it is discouraged.
All of this is said in all generality. In your specific context, eval() might be safe. However, it takes some effort to have a context safe for use of eval(), that actually needs eval().
eval() is a possible vector for cross-site scripting.
Under normal circumstances, an attacker attempting XSS might want to get script <script></script> tags past whatever encoding, filters or firewalls might be in place. If eval() is there operating on user input, it eliminates the need for script tags.
Eval is present in many malicious scripts because it helps obfuscate code and / or sneak prohibited characters past filters. For this reason, eval() is often checked for in user input. So, when using eval() you are, potentially, providing the attacker one of their necessary tools. I'm robbing a bank and I know I don't have to sneak a gun past any metal detectors because there are a bunch in the umbrella stand inside the building.
eval
Evaluates a string of JavaScript code without reference to a particular object.
If you construct an arithmetic expression as a string, you can use eval to evaluate it at a later time. For example, suppose you have a variable x. You can postpone evaluation of an expression involving x by assigning the string value of the expression, say "3 * x + 2", to a variable, and then calling eval at a later point in your script.
Be careful when using eval
eval() is a dangerous function, which executes the code it's passed with the privileges of the caller. If you run eval() with a string that could be affected by a malicious party, you may end up running malicious code on the user's machine with the permissions of your webpage / extension.
eval() provides access to the JavaScript compiler and this ultimately allows for code to be executed at a later time. The arguments passed to the function are passed to the JavaScript compiler after which the code is executed.
Developers argue about the security of eval(). It is less secure, but if you're absolutely sure your input is sanitized before passing it along, then it shouldn't be a problem.
Also, the results of eval() are generally slower because the code has not yet been compiled nor cached. There's obviously going to be a performance hit for using the function.
It's also difficult to debug code that results from the use of eval() because there is little-to-no contextual information (think line numbers) about the code that is ultimately executed.
In terms of web development, one of the current most popular uses of eval() is to deserialize a JSON string usually in the context of Ajax applications; however, this isn't to say that there aren't many other uses.
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.
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.