
A Case for Safe Eval - haburka
https://github.com/robert-j-webb/stackedit/blob/master/safe%20eval.md
======
ScottBurson
Rather than trying to sanitize the input to 'eval', why not just write a
separate expression evaluator? Then you can know for sure that there's no way
to craft an input that causes an arbitrary function call, because your
evaluator has no way to make an arbitrary function call. Also, you can define
whatever operators you like.

This use case doesn't seem to me to be one that's likely to arise often.

~~~
fjsolwmv
Everything old is new again.

[http://search.cpan.org/search?mode=module&query=Safe](http://search.cpan.org/search?mode=module&query=Safe)

~~~
jasonm23
Why stop at Perl? DSLs and limited environments are almost as old as Lisp.

------
olegbulatov
> Additionally, (() => 5)() doesn't work because we don't allow open and close
> parens next to each other.

Not really, I can write

    
    
      ((/**/)=>5)(/**/)
    

Using eval on data that are provided by a user is always a bad idea. You
cannot be sure that your sanitiser will be safe with new syntax elements.

~~~
tgsovlerkhgsel
Or simply use the replacer to generate what you want - include illegal
characters like letters, and once they get removed, you get what you want:
`((x)=>5)(x)` becomes `(()=>5)()` once the x is removed.

I don't see a way to exploit it, but that means very little (as shown by the
fact that the author missed this one).

~~~
mcphage
> include illegal characters like letters, and once they get removed

Removing characters you know to be illegal and then trying to run it anyway is
asking for trouble. If it contains illegal characters, it should fail
immediately.

------
bastawhiz
> This is very quickly becoming a huge pain for me to write. I have to write a
> compiler, and a lexer. I have to continue extending this for each new kind
> of operator added.

Counterpoint: good luck getting that magical regex to scale to many new
operators. Or other data types, for that matter. What if, for instance, you
wanted to add support for strings? Suddenly, the entire original approach is
useless and building a tiny compiler is the best and safest option.

It also the case that, as olegbulatov demonstrated, you're fighting an uphill
battle trying to sanitize code with a regex. There's really not reasonable way
to prove with 100% certainty that a regexp blocks _all possible expressions_
that could do nasty things. Writing a tiny compiler might be a pain in the
ass, but you can be very sure of the safety that it offers....and it doesn't
need to use eval().

~~~
UncleMeat
Yup. Sanitizing arbitrary expressions is even harder than sanitizing sql
expressions. After years of seeing how regex based sql injection prevention
doesn't work we shouldn't apply the same things here.

------
geofft
This seems like just another form of string trepanation:
[https://thoughtstreams.io/glyph/string-
trepanation/](https://thoughtstreams.io/glyph/string-trepanation/)

You're trying to get the evil out of the string, at which point the remainder
of the string is good. That's not how strings work. If it happens to work now,
who knows what syntax will show up next year (and there are plenty of examples
on this page of working around the regex). You're in a constant battle of
figuring out whether bytes are good or evil when the actually answer is
they're neither.

Full recognition before processing!
[http://langsec.org/occupy/](http://langsec.org/occupy/)

------
myWindoonn
In the E family, cousins to ECMAScript, the eval() function _is_ confinement-
safe and cannot access objects which it hasn't been given explicit access to
use. The proof actually can be extended to any lambda calculus which doesn't
have global mutable state.

Edit: I should add that the crux of the technique involves a two-argument
eval() with an explicit environment, as opposed to the single-argument eval()
of ECMAScript.

~~~
__s
The problem is that you need to sandbox prototypes et al

[https://en.wikipedia.org/wiki/JSFuck](https://en.wikipedia.org/wiki/JSFuck)
[https://stackoverflow.com/questions/2669690/why-does-
google-...](https://stackoverflow.com/questions/2669690/why-does-google-
prepend-while1-to-their-json-responses)

~~~
cormacrelf
I can't believe nobody else pointed out JSFuck.

Try the converter at [http://www.jsfuck.com/](http://www.jsfuck.com/) \-- or
browse the esolang entry:
[https://esolangs.org/wiki/JSFuck](https://esolangs.org/wiki/JSFuck)

Spoiler: it can do everything normal JS can. Just because it uses 20,000
characters to do a simple if statement doesn't mean your eval is 'safe'.

------
monochromatic
So we restrict it to a subset that you can’t figure out how to exploit? How
reassuring.

~~~
cipherzero
Haha... exactly. Seems questionable to me.

------
zbentley
This idea is neat, and the article does a good job of highlighting some of the
potential evil and potential good of eval. However, as many other commenters
have pointed out, it's far from a complete or trustable solution--and even if
it's good enough _for now_ , it may not be good enough for the JS of tomorrow
(this language iterates bloody _fast_ ).

Far better would be a capability-based sandbox API in the browser (like the
Node.JS one:
[https://nodejs.org/api/vm.html#vm_vm_createcontext_sandbox_o...](https://nodejs.org/api/vm.html#vm_vm_createcontext_sandbox_options))
that also supports filters like "stack depth" to limit infinite recursion,
and/or some sort of opcount to limit other nonterminating functions. Combine
those two things, and you have at least a way to secure arbitrary code you
want to eval.

I think that most people with a real/non-insane need for such a service would
be willing to pay a fair amount of performance (e.g. introducing an Erlang-
esque reduction count idea to code inside the sandbox) to get it working
safely.

Mind you, even if that existed it'd be hard to use correctly; people would try
to safe-eval code, fail, and open up over-promiscuous capabilities (just like
redis: [http://antirez.com/news/118](http://antirez.com/news/118)), putting us
back at square one. For many purposes, though, it would be a very interesting
and powerful tool when wielded carefully.

------
ghayes
While fun, I do hope this is a joke, since as other comments have said, it’s
fairly straight-forward to make a DSL for the given problems that this could
solve. Beyond that, this would never be safe from syntax mutations in new
versions of ECMAScript and sadly would cause issues similar to the recent
smoosh controversy (where a common library implemented `flatten` in a way that
was incompatible with the new ES specification).

------
ballenf
Title should be:

> A Case for Safe Eval

The author's argument is that there is at least one valid use case for eval,
not that it belongs in all web projects.

~~~
gkya
Also, "in JavaScript" can be appended to make clear that this is specific to
that programming language.

~~~
wybiral
"Using JavaScript's eval function to evaluate limited arithmetic expressions
safely"

------
kjeetgill
At this point, why not just implement a restricted interpreter in JS with a
simple language. As soon as you do anything non-trival you're going to want
variables and function calls.

~~~
rkeene2
This is similar to what Tcl does for Safe Interpreters.

[https://www.tcl.tk/man/tcl8.6/TclCmd/safe.htm](https://www.tcl.tk/man/tcl8.6/TclCmd/safe.htm)

------
cipherzero
I see why it doesn’t allow their example of “(() => 5)()”

But what is preventing me from doing “((a) => 5)(0)” since the regex only
requires at least one non space char between parens...

Or any function call really?

~~~
Groxx
`(a)` isn't allowed because letters aren't allowed, and you can't put a number
in the argument location (e.g. `((1) => 2)(3)` doesn't parse as valid JS).

~~~
spraak
A commenter above offered

((/ __/ )=>5)(/ __/ )

~~~
spraak
HN formatting is messing this up. It should have two asterisks between each
"//"

~~~
Groxx
indent 2+ spaces for code-like formatting :)

    
    
      *plenty of asterisks*
      /**/
    

Also simplifies ascii art since it's mono-spaced:

    
    
            |\__/,|   (`\
          _.|o o  |_   ) )
      ---(((---(((---------

------
robocat
Maybe the author of this could provide a web page that allows anybody to enter
"safe" JavaScript.

Provide a $10 prize for setting a cookie to value xyz, or for calling function
zyx().

Repeat until safe.. (Makes me think how bug bounties really are useful but not
that useful for security).

~~~
fjsolwmv
Need to raise the bounty quite a lot to make white hat more attractive than
black hat.

~~~
robocat
Yet lots of people do security CTF competitions ([https://ctftime.org/ctf-
wtf/](https://ctftime.org/ctf-wtf/)) for free. Offer less instead!

------
sbr464
I agree that safe eval and safe JSON parsing should be higher class citizens
in the JS world. Just curious, is this (one of the reasons) why companies like
Salesforce introduce an alternate language such as VisualForce, to have more
control over safety? It would be nice to not have to force users to learn a
new language to enable dynamic runtime features. I feel that it’s near
impossible to outsmart all bad actors, but it would be nice to enable full
user creativity by allowing access to an existing language.

------
olliej
If you really wanted to do this you could avoid a whole bunch of badness by
spawning a worker, clearing out all the timer and networking APIs and just
evaluate the script there.

Infinite loops etc could still happen but they would not significantly effect
your page and the worker gets killed when the user navigates.

As far as their regex goes I’m not sure it actually prevents the wonders of []
based programming.

~~~
olliej
Found it again — jsfuck.com does the translation automatically. I have not
tested whether it’s doable yet

------
tbodt
Python has ast.literal_eval built into the standard library, which is a pure
Python lexer/parser/evaluator for Python syntax. So you don't have to write
your own lexer and parser and so on. It's somewhat slower though, being
written in pure Python.

------
ballenf
Wouldn't doing `eval` in a service worker provide an even greater level of
safety? Might even be enough to drop some of the character restrictions, if
the use case was broader than the one here.

~~~
geofft
An answer on StackOverflow
[https://stackoverflow.com/a/26488003](https://stackoverflow.com/a/26488003)
claims that web workers count as same origin, but you can totally solve this
use case with <iframe sandbox>. (And other answers say that sites like
JSFiddle do exactly this.)

There's an example of using this for untrusted eval in the linked HTML5Rocks
post:
[https://www.html5rocks.com/en/tutorials/security/sandboxed-i...](https://www.html5rocks.com/en/tutorials/security/sandboxed-
iframes/) The amount of code involved is pretty minimal and doesn't involve
opaque regexes, so something like that is probably the actual best option for
someone who wants safe eval.

That StackOverflow user also has their own library that does this using a
sandboxed iframe _and_ a web worker:
[https://github.com/asvd/jailed](https://github.com/asvd/jailed)

