5

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?

Shnick
  • 1,199
  • 1
  • 13
  • 32

2 Answers2

6

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
})();
Nick Parsons
  • 45,728
  • 6
  • 46
  • 64
Indiana Kernick
  • 5,041
  • 2
  • 20
  • 50
1

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.

Lawrence Cherone
  • 46,049
  • 7
  • 62
  • 106
Thomas
  • 11,958
  • 1
  • 14
  • 23