Search code examples
javascriptlispevalmetaprogramming

Why isn't eval a bad idea outside of JavaScript?


In JavaScript, eval is considered to be a bad idea mainly because it opens up your code to injection attacks. To quote this comment:

The attacks we are trying to avoid are when user provided values get saved, then later placed into javascript and eval'd. For example, I might set my username to: badHackerGuy'); doMaliciousThings(); and if you take my username, concat it into some script and eval it in other people's browsers then I can run any javascript I want on their machines (e.g. force them to +1 my posts, post their data to my server, etc.)

But why is this problem limited to JavaScript? Yes, JavaScript is the only language that runs on browsers, but there are contexts other than browsers where you want to avoid injection attacks.

(This part is tangential to my actual question, but people talk about the extreme, awesome power of metaprogramming. eval allows you to do metaprogramming and it sounds like the injection attack issue is the only big downside. Why is considered to be clear that the downside outweighs the upside?)


Solution

  • I can only speak for Lisp but the answer is that your assumption is false: eval is, almost always, an extremely bad idea in Lisp code.

    If you see (eval x) in a piece of code then unless you have detailed knowledge of what x is, you have no idea what this does: it can do anything (almost) that the Lisp system can do. If x contains, or may contain, untrusted data, then that's just a really, really bad idea for very obvious reasons.

    You could make this safe by writing some kind of walker which looks at x and checks it is safe. But ... that walker is within epsilon of being an evaluator for your safe subset of Lisp: just use it, instead!

    eval has uses in Lisp, so it is good that it is in the language, but the cases where you need it are few and far between: there are cases where you wish to be able to evaluate arbitrary Lisp source code, such as writing a development environment, but they're very, very rare.

    In particular do not conflate (in Lisp) eval with metaprogramming. In Lisp the traditional approach to metaprogramming (I say 'traditional' because Scheme is arguably not, quite, like this) is rooted in two ideas:

    1. the source code of the language is representable as a suitably low-commitment data structure in the language itself;
    2. You can write functions, called macros, which will map from instances of this source code representation to other instances.

    There is much more to it than this of course, but note: this does not require eval.