Friday, June 27th, 2008
eval(’foo=a’, obj.fn); How you aren’t private in Firefox
Peter Michaux has found the magical eval(..., context) method available in the Firefox implementation. This means that you can't create truly private data:
-
-
// Getting "private" variables
-
var obj = (function() {
-
var a = 21;
-
return {
-
// public function must reference 'a'
-
fn: function() {a;}
-
};
-
})();
-
-
var foo;
-
eval('foo=a', obj.fn);
-
console.log(foo); // 21
-
-
// Setting "private" variables
-
var obj = (function() {
-
var a = 21;
-
return {
-
getA: function(){return a;},
-
alertA: function(){alert(a);}
-
};
-
})();
-
-
console.log(obj.getA()); //21
-
eval('a=3', obj.getA);
-
console.log(obj.getA()); // 3
-
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.












I wonder what implications this might have for Google’s Caja project.
I wonder what implications this might have for Google’s Caja project.
I wonder what implications this might have for Google’s Caja project.
I wonder what implications this might have for Google’s Caja project.
.oO(hey, this is fun)
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
@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!
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.
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.
@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.
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
@PeterMichaux:
How about just:
eval = function() {alert(”eval not allowed!”);};
eval(”alert(’evaled!’);”); //eval not allowed
delete eval;
eval(”alert(’evaled!’);”); //evaled
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”.
@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)
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.
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
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;}
};
})();
nm… still suffers from the darn “delete”
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.
so, in other words, they are “protected”, not “private”. sucks, but not as much as “public”. :)
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. :)
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);
“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.
@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. :(
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);
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
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;
}
}
}
}();
@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. :(
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.
Just an FYI from Brendan:
http://groups.google.com/group/google-caja-discuss/msg/ead8d8597a22c013
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.