My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

Wednesday, July 02, 2008

SCARY EVAL ... and the "futuristic" solution

Planet Earth, Year 2000

eval("document." + myformName + "." + myInputName + ".value");



Satellite Moon, Year 2004

eval is evil, nobody should use them, "except for json evaluation"



Planet Earth 2.0, Year 2008
OMG somebody could penetrate my closures!!!


Web Reflection, few days later
First of all, it was in the MDC, so if everbody is surprised, it is because we did not read that page, or simply we did not try that code (please correct that page, the second argument is everything but not the same of with statement).

Secondly, please, DO NOT CHANGE THAT FEATURE!!!
What I mean is that JavaScript is an amazing language, where everything could be changed, thanks to its intrinsically dynamic nature.

If we are worried about this fake news:

  • we should worry, before, about XSS in our site (after SQL injections)

  • if we are sure about missed XSS possibility, we should be worried about external libraries

  • if we are sure about third parts libraries, we should be worried about JSON

  • if we are sure about external libraries and JSON interactions ... why on heart should we be worried about this historic feature?



The feature is present in Gecko, but hey, Gecko implements constants!!!
Another thing I could not understand, is why somebody opened a bug for a feature that could be AMAZING if used for our own purpose.

Code and closure introspections, protected method implementation, whatever I cannot imagine right now.

On the other hand, it is extremely simple to solve the problem using a basic constant declaration

// webreflection "from the space"
try{eval("const $eval=function(eval){return function($eval){return eval($eval)}}(eval)")}catch($){$eval=eval};


Now try to delete the constant, try to do whatever you want in FireFox, and if it will be possible to change that $eval behaviour with Gecko browser, that will be the only bug you should really fix.

With Best Regards,
your bloody evil propagator ( booh! )



Update
Since I am writing in the bug page, I think my example could be interesting to understand what we are going to loose when this feature will disappear:

// sandbox: redefined environment
var myEnvironMent = {
eval:function(script){
return eval(script);
},
alert:function(String){
(document.body || document.documentElement)
.appendChild(document.createTextNode(String));
}
};

// evaluate a script from a string or a textarea
eval("alert('Hello World')", myEnvironMent);

/* you cannot do this with a with statement
with(myEnvironMent)
eval("alert('Hello World')");
*/



// privileged protected methods in prototype
Protected = function(){
function _showMessage(message){
alert(this.name.concat(" say ", message));
}
return function(name){
this.name = name;
};
}();
Protected.prototype._ = function(method){
return eval("_" + method, this)
.apply(this, Array.prototype.slice.call(arguments, 1));
};

var p = new Protected("Andrea");
p._("showMessage", "Hello World");

These are only two quick examples, and I wrote them in 1 minute.
Now try to imagine what could be possible to do having such control over closures!

7 comments:

getify said...

Indeed, it appears you have found a way to prevent the eval from working.

What I'd like to do is provide a "better" eval where by default it prevents access to private closure, but an object can "register" itself or grant such access to eval, so that the helpful uses you refer to would be preserved, but unwanted access to this "scary eval" could be stopped.

Or maybe, it should be the other way around, that by default it allows all access, but objects can "opt out" of the scary eval if they want to remain private.

Peter Michaux said...

I don't see how you have a solution to the pseudo-security problem. Even with your code between the closure and the third party code, the third party code can still read inside the closure.

var obj = (function() {
var a = 21;
return {
// public function must reference 'a'
fn: function() {a;}
};
})();

try{
eval("const $eval=function(eval){return function($eval){return eval($eval)}}(eval)")
}
catch($) {
$eval = eval
}


var foo;
eval('foo=a', obj.fn);
console.log(foo); // 21

Your idea also doesn't account for possible ECMAScript implementation that do have the second parameter to "eval" but do not have "const".

I think trying to secure JavaScript and letting any third party code on to the page will be insecure. Implementations are free to do anything (insecure) that they want to do. That means they will be doing things you don't know about and haven't "patched" but that hackers do know and can exploit.

The sub-setting ideas of Caja and ADSafe are likely much more likely to succeed.

Anonymous said...

Uhm, sorry, I don't get it. What exactly did you accomplish with $eval?

Andrea Giammarchi said...

Guys, you are right, I did not explain perfectly my point of view.

Since "nobody" uses second argument, and since for some understandable reason now "everybody" is scared about them, I showed you how you cold define your own eval function, in my case $eval, and use them instead of native.

This means that if every library will use them, we will be sure that nobody could read our closures.

Redefining global eval = $eval will give us extra "security", so you have to delete eval to go back in the native one, but at the same time you can pre-parse every script or code in your page that uses eval instead of $eval (or simply replace that string wth your favourite editor).

I would like to be able to redeclare global eval instead of loose its powerful second argument, but whatever is my vision, the point is that there is no security in JavaScript, as an embed runtime interpreted language, and that that feature open a lot of doors, while we are going to close them.

Whatever you write, in JavaScript, could be redefined, constants a part, so even if we will not have that possibility, everybody could rewrite entirely an Array constructor or an entire library, letting us feel so secure about public methods, that could be simply wrappers with evil code.

So, the point is: you can even definitively remove eval function, but if you are scared about them, it means that your site suffer XSS, and eval problem is only one of thousands.

I will write another post to demonstrate how powerful and useful could be the second argument, and why I do not want to loose them.

Regards

Andrea Giammarchi said...

please guys, lets talk about eval in my last post, thank you :)

Anonymous said...

I'm the same anonymous as before. I didn't write any example code because I was afraid I was missing something critical, but still I don't see it:

function encloseContext() {
var a = "secret"
return function() { return a.substring(1) }
}
s = encloseContext()
alert($eval("eval('a', s)"))


Worked just fine here. Firefox 3.0, Ubuntu Linux.

Andrea Giammarchi said...

as I said, with introduction of a global constant like $eval, every script, strings included, should not contain eval.

Please read the next post, and talk about this eval feature there, thank you :)