Module Pattern Provides No Privacy...at least not in JavaScript(TM)
The module pattern has been discussed many times and has shown how ECMAScript has the ability to encapsulate data as "private" variables by using closures.
Today, in a comment on my blog, a reader, haysmark, points out that Mozilla's JavaScript(TM), the implementation in Firefox, has a second argument extension to eval
that allows external code to spy on otherwise private variables.
Try the examples below in Firefox.
// 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
This use of eval
delivers a blow to the usefulness of statements like "JavaScript provides the means to construct durable objects
that can perfectly guard their state by using a variation of the Module Pattern." Perhaps an ECMAScript implementation with no extensions can provide such security but one of the most important implementations, JavaScript(TM) in Firefox, apparently does not.
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.
Thanks to haysmark for the comment today. A big part of the reason I blog is to put ideas out there to see if they are shot down. I'm interested to see if this idea is shot down.
Update June 27, 2008 This post has been discussed at some other places: Caja Discussion Group, Ajaxian, Simon Willison's Blog
Update July 2, 2008 Apparently this post mattered and the second argument to eval
will be gone in Firefox 3.1. More discussion on Douglas Crockford's Blog, Ajaxian, and John Resig's blog.
I didn't necessarily think JavaScript(TM) should be changed at all. Patching this security hole doesn't make JavaScript "secure". Since there are so many ECMAScript implementations, the ECMAScript specification allows for implementation extensions to the language, and there is no governing body certifying all publicly available implementations as "secure", the language will never be secure. I just thought this example was kind of a neat oddity and it seemed many others found this surprising as well. If security is a concern then ensure all the code you allow in your page from third parties is safe. Projects which define a safe subset of JavaScript and/or automate checking of third party code like Caja and ADSafe are more likely the way forward. I have also been thinking legal business agreements would be a good idea before allowing third party script on pages.
Comments
Have something to write? Comment on this article.
Matt,
I wondered the very same thing. This is why I added the obj.alertA()
part of the second test. It looks like it is really changing a
.
I'm working on a project that depends on sandboxing code using Spidermonkey's ability to provide multiple Contexts in a single JS user-space. Eg I'm executing end-user functions in a sandboxed enviroment, and passing them a limited-use HTTP function. I'll have to test that they can't use the eval capability to remove the limits (coded in Javascript outside their context) by replacing them with a permissive function.
Thanks for the heads up on a possible exploit.
Hmm.... Couldn't you just wrap the not-really-private-context in another context? to make it truely private?
Try substituting this in the example provided by haysmark. I've been working with object variations similar to the following:
var Person = function (first, age) {
var _p=function(){}; //private object scope
var p=function(){}; //public object scope
_p.first = first;
p.getFirstName = function() {
// captialize the name
return _p.first.charAt(0).toUpperCase() + _p.first.substr(1);
};
};
_p.year = age;
p.getAge = function() {
return _p.year;
};
p.sayInfo = function() {
alert(p.getFirstName() + ' ' + p.getAge());
};
return p;
Saying "It just doesn't provide any security" is a bit too strong. If you have to use eval with two arguments, you know you're doing something you shouldn't do. It's like saying "A lock on a door doesn't provide any security because you can use a crowbar to open the door".
This is not a new problem, as Brendan explains at
http://groups.google.com/group/google-caja-discuss/msg/ead8d8597a22c013
Peter. This is a really good find. I tried out a few examples myself, and basically the only way to get yourself out of this loophole is to hide your entire implementation code into another (anonymous) closure. Nevertheless, good find :) Thanks for the tip.
I haven't used eval() in the real world since YUI released its JSON tool, but I wasn't aware of the context parameter, so this is good info. Thanks.
Why should not every browser vendor implement their own version of javascript? I mean, Mozilla has it, Microsoft has it... when everyone else will come up with their own versions too?
I mean, hell, why not? Instead of following the standard let's have dozens of javascript versions so when you write a simple js code you should add a note that: "this script only works great in FF. In IE and Opera fails miserably"...
Why does it have to be so complicated? Why don't they KISS??
"It just doesn't provide any security" is a bit too strong.
Especially since at the bottom of this (in most contexts, anyway), we're talking about interpreted source code.
I suspect the best enforcement mechanism isn't strictly technical, but lies in the process of refining the object interface until it removes most of the temptation to mess about with the internals.
By the same token, you can use reflection to modify private members in Java as well. Basically, you cannot expect to run arbitrary code in the same VM/interpreter and expect to have any distinction of privileges. Private members serve more to guide the programmer about what is part of the public interface for an object, and what is not. This does not make this programming pattern any less useful.
All this really proves is that eval is evil. No news there.
The takeaway is, if you're using eval, stop. If you're running untrusted JS, run it through Caja or AdSafe, and using an iframe on a different domain name is probably a good idea, too.
By the way, I don't think "JavaScript" is trademarked by anyone.
I disagree with all the "eval
is evil" rhetoric. eval
makes it possible to write some very efficient code. For example, eval
makes for efficient parsing of JSON, compiling a CSS selector string (or any domain-specific language) into a JavaScript function. eval
opens up other possibilities for meta-programming in JavaScript that would otherwise be impossible. eval
makes writing a JavaScript REPL possible.
The idea that "eval
is evil" doesn't seem to appear so much in communities which use eval
where it helps and not where it hurts. The Lisp community seems to have a much better perspective on the use of eval
.
I've always found the idea of data "security" in client-side JavaScript a bit silly. After all, it's code and data that sits on the client machine.
I've never run into any instance where private variables in JS were actually necessary so this realization doesn't bother me much.
Have something to write? Comment on this article.
Peter, does it actually change the value of (a) or does it change the value of (a) returned by the getter function?