Friday, June 27th, 2008

eval(‘foo=a’, obj.fn); How you aren’t private in Firefox

Category: JavaScript

<p>Peter Michaux has found the magical eval(…, context) method available in the Firefox implementation. This means that you can’t create truly private data:

javascript
< view plain text >
  1. // Getting "private" variables
  2. var obj = (function() {
  3.   var a = 21;
  4.   return {
  5.     // public function must reference 'a'
  6.     fn: function() {a;}
  7.   };
  8. })();
  9.  
  10. var foo;
  11. eval('foo=a', obj.fn);
  12. console.log(foo); // 21
  13.  
  14. // Setting "private" variables
  15. var obj = (function() {
  16.   var a = 21;
  17.   return {
  18.     getA: function(){return a;},
  19.     alertA: function(){alert(a);}
  20.   };
  21. })();
  22.  
  23. console.log(obj.getA()); //21
  24. eval('a=3', obj.getA);
  25. console.log(obj.getA()); // 3
  26. obj.alertA(); // 3

Of course, this is the way of dynamic languages and there is still value:

This use of the eval, however, doesn’t make the module pattern useless. Its primary benefits are modularizing code so similarly named variables are not colliding and protects you or other developers from accidentally violating a programming interface. The module pattern also makes it possible to do OOP-like things without using keywords new, this and prototype which generally makes code more robust.

So the module pattern is still good. It just doesn’t provide any security in a major browser.

Related Content:

Posted by Dion Almaer at 12:42 am
30 Comments

++++-
4 rating from 29 votes

30 Comments »

Comments feed TrackBack URI

I wonder what implications this might have for Google’s Caja project.

Comment by cromwellian — June 27, 2008

I wonder what implications this might have for Google’s Caja project.

Comment by cromwellian — June 27, 2008

I wonder what implications this might have for Google’s Caja project.

Comment by Jamie — June 27, 2008

I wonder what implications this might have for Google’s Caja project.

.oO(hey, this is fun)

Comment by SkaveRat — June 27, 2008

What about that?

(function(){
var _eval = eval;
eval = function(str){
_eval(str);
}
})()

var obj = (function() {
var a = 21;
return {
fn: function() {a;}
};
})();

var foo;
eval(‘foo=a’, obj.fn);
alert(foo); // a is not defined

Comment by nuxodin — June 27, 2008

@nuxodin-
yeah, looks like the dynamicism (is that a word?) of the language lets us disable this “context” feature of the eval statement, so we can protect our modules if we want to. very good point!

Comment by shadedecho — June 27, 2008

PS. Inspired by nuxodin’s post, I’m working right now on a better “eval” replacement that will not totally discard this functionality, but instead allow a clean, flexible way where modules can opt themselves out of this functionality, whereby the “context” parameter of eval will still work, but only against an object that has not specifically opted itself out of that. This allows “modules” that need privacy to maintain it, but it preserves other useful patterns for this eval-context.

Comment by shadedecho — June 27, 2008

Hm. Am I to understand that people have been trying to figure out ways to make javascript secure … in the client? That’s funnier than the (brilliant) join the Caja game, earlier in the thread, as all solutions looking for problems are.

Comment by sandro — June 27, 2008

@sando-no, that’s a very miopic view of what this is talking about. But certainly, if we can make javascript objects which behave more like proper classes (hiding, protecting, etc), it’s a better pattern for the language and for the JS community as a whole. The post to show that eval had a backdoor around the pattern was a good one, but thankfully there’s a way to lock the backdoor if you want to.

Plus, if we make a JS class object that uses the “module” pattern, it’s more likely it will behave as intended even in cases where other scripts, frameworks, or just malicious hackers try to muck with it. How is that not a good thing to try to achieve?

There’s a big difference between *security* (which you’re right, no JS can provide) and *stability*, which this pattern does offer a good stab at.

Comment by shadedecho — June 27, 2008

nuxodin,

In at least some cases, there is a way to restore eval so it takes a second argument again.

(function() {
var _eval = eval;
eval = function(str) {
_eval(str);
}
})();

var obj = (function() {
var a = 21;
return {
fn: function() {a;}
};
})();

// restore original eval
var div = document.createElement(‘div’);
div.innerHTML = ”;
document.body.appendChild(div);
eval = window.frames.hacker.eval;

var foo;
eval(‘foo=a’, obj.fn);
alert(foo); // 21

Comment by PeterMichaux — June 27, 2008

@PeterMichaux:
How about just:
eval = function() {alert(“eval not allowed!”);};
eval(“alert(‘evaled!’);”); //eval not allowed
delete eval;
eval(“alert(‘evaled!’);”); //evaled

Comment by Jordan — June 27, 2008

Jordan,

Yours is tidier and “delete” is a language operator so would be hard or impossible to secure. There are probably many ways to workaround any patch attempting to secure “eval”.

Comment by PeterMichaux — June 27, 2008

@Jordan

You are right, its not possible to make it not work.

I tried this:
(function() {
var _eval = eval;
var s_eval = eval = function(str) {
_eval(str);
}
this.__defineGetter__(“eval”, function(){
return s_eval;
});
window.__defineSetter__(“eval”, function(val){
alert(‘not allowed’)
});
})();

but you can allways:

document.getElementById(‘test’).contentWindow.eval(‘foo=a’, obj.fn)

Comment by nuxodin — June 27, 2008

I wonder if we could somehow overwrite the prototype (maybe Object.prototype) so that even newly created objects don’t have the old eval but rather have the new “secure” eval instead.

Comment by shadedecho — June 27, 2008

or what about this:

(function(){
var _eval = eval;
var s_eval = eval = function(str) {
_eval(str);
}
this.__defineGetter__(“eval”, function(){
return s_eval;
});
window.__defineSetter__(“eval”, function(val){
alert(‘not allowed’);
});
var _oldCE = document.createElement;
document.createElement = function(type) {
var obj = _oldCE(type);
obj.contentWindow.eval = s_eval;
}
window.eval = s_eval;
(function recurseDOM(_root) {
if (typeof _root === undefined || _root === null) return;

try {
for (var i=0; i

Comment by shadedecho — June 27, 2008

oops… here’s the rest:

< _root.childNodes.length; i++) {
try { _root[i].eval = s_eval; _recurseDOM(_root[i]); } catch (err) { }
}
}
catch (err2) { }
})(document.getElementsByTagName(“html”));

})();

var obj = (function() {
var a = 21;
return {
fn: function() {a;}
};
})();

Comment by shadedecho — June 27, 2008

nm… still suffers from the darn “delete”

Comment by shadedecho — June 27, 2008

IMPORTANT: it should be pointed out that even though it appears that someone can gain access to the VALUES of the modules “private” members, it appears to only give a read-only access, or copy thereof, because it does not appear you can affect the value of either an internal variable (like “a” above) or overwrite a protected function.

This means that the “security” vulnerability is significantly abated in that all a hacker can do is see what the values are (which they can probably do by view-source anyway!) but they cannot change the values, at least from what I’ve tested so far.

Comment by shadedecho — June 27, 2008

so, in other words, they are “protected”, not “private”. sucks, but not as much as “public”. :)

Comment by shadedecho — June 27, 2008

nm, AGAIN… i hang my head in shame and retreat. I rang the winner’s bell way too early.

it now appears from my further testing that in fact this dang parameter gives full access to the entire *private* scope, regardless of the presence of closure or anything. just one publicly exposed function, even an anonymous, empty one without any closure ties to the internals, grants eval() full read/write access to the entire internal scope of your module. ugh. i give up. :)

Comment by shadedecho — June 27, 2008

umm…why not just do this?

var obj = (function() {
var b = 21;
function a(){
return b;
}
return {
fn: a
};
})();

var foo;
eval(‘foo=a’, obj.fn);
alert(foo);

Comment by TNO — June 27, 2008

“It just doesn’t provide any security in a major browser.” So the lesson is to never assume you have security in scripts like these, which we already knew.

Comment by richtaur — June 27, 2008

@TNO-
The point is that a malicious or prying person CAN do this with your object (even though you provided them with a getter function, they can end-around you):

eval(‘foo=b’,obj.fn) and get directly at your internal, “private” variable b, either gettings it’s value into foo, or eval(‘b=500′,obj.fn) and change your internal variable without you being in control. This is the bad thing. :(

Comment by shadedecho — June 28, 2008

Well, I guess you could just nest another object within it:

var obj = (function() {
var Private = function(){
var a = 21;

return {
getA : function(){
return a;
}
}
}

return {
fn: function(){
return Private.getA();
}
}

})();

var foo;
eval(“foo=Private”, obj.fn);
alert(foo);

Comment by TNO — June 28, 2008

Nevermind, even digging a deeper object hole just involved a few extra steps assigning pointers before assigning the variable.

var obj = function(){
var Priv = function(){
var a = 21;

return {
getA : function(){
return a;
}
}
}();

return {
fn: function(){
return Priv.getA()
}
}
}();

alert(obj.fn()) //21
var foo;
eval(“foo=Priv”, obj.fn);
alert(foo); //[object Object]
alert(foo.getA()) //21
eval(“foo=a”,foo.getA)
alert(foo) //21

Comment by TNO — June 28, 2008

Another attempt:

var obj = function(){
var a = 21;
return {
prop : undefined,
get prop(){return a},
set prop(x){
if ((typeof x === “function”) || (typeof x === object)) {
return false;
}
else {
a = x;
}
}
}
}();

Comment by TNO — June 28, 2008

@TNO-
This is very interesting, you indeed seem to have shown how this allows controlled read/write access to the internal variable “a”. That’s a good start.

But, two things: First of all, this syntax is incompatible with IE. So, to use it, all developers would have to browser sniff and return a different patterned public API for IE users (which don’t have the dang eval problem) than for FF and others. This could work.

But also, and more troubling, would be that the public API for a module could not contain anything other than functions which set or retrieved values, using the pattern you’ve suggested. For instance, general functions which were intended to be able to have several parameters passed to them, and have some kind of operations done on them, would not be possible, as they would automatically open up the eval vulnerability. But, the set prop() functionality could only be used to pass in a single parameter, which means that any function which needed to pass in multiple values would have to do so by wrapping them in an array or object syntax. This would mean that to keep the calling code from having to have browser-specific functioning, the API calls would all have to be kind of awkward, using the object/array wrap syntax for all parameters.

Both these limitations seem possible to leverage for most “modules”. But it certainly makes things a lot uglier. :(

Comment by shadedecho — June 29, 2008

I think too big a deal is being made of this. There has never been any safety in this language. That was not one of its goals. But experiments with ADsafe and Caja show that the language can be subsetted to provide safety. A safe subset must block access to the global object. The eval function provides such access, so it is already excluded by all safe subsets. Peter’s report is shocking, but realistically, things are no worse. If you load third party code on your page and that code can use any form of eval, then you and your customers are screwed no matter what browser they are using.

Comment by crock — June 30, 2008

Just an FYI from Brendan:

http://groups.google.com/group/google-caja-discuss/msg/ead8d8597a22c013

Comment by TNO — June 30, 2008

FYI: Andrea Giammarchi “solved” this problem here:

http://webreflection.blogspot.com/2008/07/scary-eval-and-futuristic-solution.html

I can’t find any holes in the solution, yet. But with this topic, I’ve already been burned by speaking too soon, so I’m cautiously optimistic.

Also, apparently FF3.1 is going to disable this “bug” (ie, feature), to the dismay of some, including Andrea. I agree that it should be kept and has some really interesting benefits for introspection of closures and such. Again, my original idea is that we could make a “smarter” eval where it would default to allowing access, but objects instances could specifically opt themselves out in some way, and the smart eval would respect that request.

That way, most objects could benefit from it, and those which really need to keep things “private” for some reason could in fact do that.

Comment by shadedecho — July 2, 2008

Leave a comment

You must be logged in to post a comment.