7

It's a well known fact that neither Javascript's eval keyword nor Function objects created from strings should ever for any reason be used to run untrusted code.

However, I'm wondering if ES6 proxies change that. Consider:

let env = {eval: eval};
let proxy = new Proxy(env, { has: () => true });
with(proxy) {eval('...')}

The proxy object pretends to have all possible properties, which means that it blocks the search of higher scopes. Within the with block, any properties not set on env appear undefined, and any global properties set inside the with block are actually set on env.

This seems to allow me to set up a completely controlled and isolated environment for the evaled code to run in. What are the risks?

Here are a few concerns I can see:

  1. Don't put anything that references window, or document, or localStorage, or anything else sensitive, into env.
  2. Don't put any mutable object into env unless you're ok with untrusted code mutating it.
    • Solution: make deep copies if necessary.
  3. Code inside the with block has no access to anything outside it. If it needs things like Math, Object, or String, they have to be put in env - which means these can be modified by malicious code. Even the eval function in my minimal example above can be modified.
    • Solution: Create proxies for these objects to white-list read-only access to specific properties.

As long as you follow these guidelines, is this actually safe? Are there other concerns?

4
  • 2
    while(true);. Commented Jun 21, 2017 at 3:33
  • 1
    You also forgot access to this (….window, .document, .anything) and (function(){}).constructor("…")() Commented Jun 21, 2017 at 3:36
  • @Dandan is doing a very similar thing. Do you work together? Commented Jun 21, 2017 at 3:39
  • 2
    Because I have fun with messing around: ({}).constructor.defineProperty({}.constructor.getPrototypeOf({}),"get",{value(){this.has=()=>false;this.get=undefined;},writable:true}); whoops; document.write("I am a bad guy. Good luck!") Commented Jun 21, 2017 at 4:12

1 Answer 1

3

It is quite easy to break out of this environment, via a number of different ways, some or all of which might possibly be mitigated:

  1. Object, Array, and RegExp literals ({ }, [ ], and /.../) are unimpeded by the Proxy and allow access to (and mutation of) Object.protoype, Array.prototype, and RegExp.prototype. You can, however, lock these with Object.freeze before running your eval.

  2. You must delete env.eval within your evaled string, or else the script can execute global code by renaming the eval function like globalEval = eval;

  3. You cannot prevent the creation of new functions, which may use a global this object: (function () { this.globalFunc(); })(). Possibly enforcing strict mode by appending "use strict"; to your evaled input could eliminate this escape vector.

  4. Any access to the Function constructor (via (a=>a).__proto__.constructor) allows execution of global code. You can delete Function.constructor to prevent this, but there may be other ways to access Function.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.