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.
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?
-
As mentioned in the docs its about scopeLawrence Cherone– Lawrence Cherone2019年03月09日 07:40:46 +00:00Commented Mar 9, 2019 at 7:40
-
@LawrenceCherone Hi Lawrence, I'm aware of that. But what does having access to the scope actually mean? ThanksShnick– Shnick2019年03月09日 07:45:08 +00:00Commented Mar 9, 2019 at 7:45
-
1jsbin.com/zimerutowe/edit?js,console,outputLawrence Cherone– Lawrence Cherone2019年03月09日 07:46:14 +00:00Commented Mar 9, 2019 at 7:46
2 Answers 2
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
})();
Comments
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.