-1

I understand at a high level why one would not want to allow arbitrary code to execute in a web browser via the JS eval() function.

But I wonder if there are any practical approaches to preventing attacks by parsing the code that is passed to eval() to check that it is safe. For example:

  • disallowing any flow control functions, e.g. for, while. (Should stop infinite loops)
  • disallowing any variable names / function calls that don't match a whitelist. (Should stop any access to the DOM, built-in APIs, or malicious functions)

If you don't think this can be done safely, could you describe the predicted pitfalls? It's valuable to me if somebody says "this isn't practical because X" rather than just some blanket statement. Trust me - if I can't convince myself with certainty that it can be done safely, I won't do it.

I know that I can write my own my expression evaluator or use a 3rd-party library that does the same. And I may do that. But I remain interested in using eval() because of certain advantages - native implementation performance and language consistency.

Erik Hermansen
  • 2,200
  • 3
  • 21
  • 41
  • 3
    If you've created your own JavaScript interpreter then maybe you could. Are you positive you've addressed every possibility? How do you prove that? – stdunbar Jan 30 '23 at 01:45
  • It's not out of the question that I might sit down with the JS spec and read through it carefully to create a proof. Please don't dismiss my effort out of hand. I am asking for early evidence/insights that point quickly to "YES" or "NO", rather than "you probably don't know what you're doing, and should give up." – Erik Hermansen Jan 30 '23 at 01:49
  • 1
    Understood. I'm not questioning your abilities but I'm simply pointing out the (27 years X how many engineers) of the existence of JavaScript and the overall community hesitance. It's a hard problem to solve - and the community would love it if you could. I don't have empirical evidence that it's not possible - give it a shot. But prove that you're right too. And will `eval()` be so emaciated by the time that you're done that it is no longer an attractive idea? – stdunbar Jan 30 '23 at 01:56
  • I'm curious, you said *"because of certain advantages - native implementation performance and language consistency"*. Correct me if I'm wrong, but direct eval's performance is always worse (than no eval), isn't it? – Gerardo Furtado Jan 30 '23 at 01:59
  • @GerardoFurtado It's not. `eval` will be superior in performance to an interpreter. – Bergi Jan 30 '23 at 02:09
  • I used `eval()` as a way to load the data on my previous project without a need to use any specific data format. We passed our own `require()` function in the context and masked vulnerable global names like `window`. In the end it was a good way to let the data chose how they want to be loaded. Some files stored strings with compressed data and then decompress them with zlib. Other files utilize loops and branching. In all cases we got an actual class instances after `eval()`, not some data structures that we still have to process. But that this specific project didn't have security concenrs – hopeless-programmer Jan 30 '23 at 02:12
  • 1
    @ErikHermansen "*I might sit down with the JS spec and read through it carefully to create a proof.*" - tbh, you do not seem to be very familiar with this topic (having to ask this question) so that I doubt you have the necessary knowledge and tooling to be able to provide a rigorous security proof. Ask Mark E. Miller how many years (oh, decades) he has spent studying this stuff… – Bergi Jan 30 '23 at 02:13
  • 1
    define "safe" thoroughly. When you say "attacks", do you mean every single possible attack to any existing and future system? You even hint that safety can conflict with getting things done. What tolerance do you have for tradeoffs in answers to your question? Define "malicious function". What makes a function malicious? Do you think a language and API designer wakes up and says to themselves- "Hm, yes, I think I'm going to make a malicious function today and put it in my standard library"? There are functions that make easier footguns; that doesn't make them morally "evil". – starball Jan 30 '23 at 02:13
  • @Bergi This is interesting, every source I've read so far (eg MDN) says it's performance is worse. Do you have any link I can read? Thanks. – Gerardo Furtado Jan 30 '23 at 02:20
  • @Bergi I am familiar with this topic good enough and my opinion is still different from the most in the community. These people are afraid of cars and planes, so they don't drive and fly instead of improving the language. Your objection is ad hominem btw. – hopeless-programmer Jan 30 '23 at 02:24
  • @GerardoFurtado Performance of `eval`uating code in a string is worse than just executing code that is part of the source, but if your goal is to load and interpret a dynamic string, you don't have an alternative. – Bergi Jan 30 '23 at 02:25
  • 2
    @hopeless-programmer I do not fear `eval` either, I know perfectly fine when it is ok and appropriate to use it (or the `Function` constructor). But I also know enough about the topic of security to say that static analysis to prove a snippet of JS to be "safe" is hard, harder than one might think. I would personally not feel capable of it, and for those who are, it is still a multi-year journey - a journey that I do not expect the OP be willing to take (I might be wrong on this), but a journey in which any shortcut will lead to a security vulnerability. – Bergi Jan 30 '23 at 02:32
  • Even focusing in on the problem of disallowing function calls that don't match a whitelist- how do you want that to happen? You need to clarify. Do you want the modified `eval` to detect calls to that function in the expression tring and avoid running altogether? That's a difficult problem. If the function is in the global scope, then it can be called as `fn()`, `window["fn"]`, `window['fn']`, `globalThis["fn"]`, `window[fnThatReturnsStrWindow()][fnThatReturnsStrFn()]`, etc. – starball Jan 30 '23 at 02:40
  • Or your modified `eval` could be defined to run the expression in a modified environment where that function is redefined to do nothing (error appears earlier and error handling, propagation, response is different). You haven't even specified what you want with that tiny part of your question. – starball Jan 30 '23 at 02:42
  • 1
    Related: [Why is using the JavaScript eval function a bad idea?](/q/86513) and [When is JavaScript's eval() not evil?](/q/197769). – starball Jan 30 '23 at 02:53
  • Look into web workers: https://stackoverflow.com/a/24028058/7475450 – Peter Thoeny Jan 30 '23 at 04:04
  • I'm starting to think this question is to broad (or maybe that's just a symptom of it lacking enough detail). web workers can be a useful tool in isolating things, but they rule out a lot of APIs by default, so to access them, you need to do message passing with the main thread. It's not clear yet whether that's a good solution because it's not clear what the problem to be solve is. – starball Jan 30 '23 at 04:08
  • @PeterThoeny Web workers are not a security/safety solution. – Bergi Jan 30 '23 at 05:08
  • @Bergi: They can be terminated if something goes wrong, one of the OP's concern – Peter Thoeny Jan 30 '23 at 19:00

1 Answers1

2

Yes, in general this is possible - basically you develop your own programming language that you know does only safe operations, you write your own parser for it, you write your own interpreter for it, and then you optimise that interpreter into a compiler targeting JavaScript that runs the result through eval.

However, using JavaScript as the base language and then stripping away unsafe parts, or even whitelisting some things, is not a good approach. The "whitelisting" would need to be sophisticated enough that starting to develop your own language is generally simpler. The two example restriction you've presented in your question fail to reach their goals: to avoid infinite execution you also need to prevent recursion, and to prevent access to builtins you more or less also need to prevent dynamic property access. A lot of work has been done to define a proven "safe" subset of ECMAScript that one could fearlessly evaluate, and believe me, it is far from trivial.

So no, this is not a practical approach.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • "_basically you develop your own programming language that you know does only safe operations_" I'd wager that's a pretty significant deviation from what the asker is looking for :P considering they wrote "_But I remain interested in using `eval()` because of certain advantages - native implementation performance and **language consistency**_" (emphasis added). – starball Jan 30 '23 at 02:46
  • @user Yes, that's what the rest of the answer is about: this is not practical. – Bergi Jan 30 '23 at 02:48