12.15.2009

JavaScript? Hello? Is anyone there?

So I am still in the architectural design phase of ESAPI4JS, and have come across an interesting problem. Well, to be honest, there are lots of problems with JavaScript and trying to make it secure - but there is one that I have yet to find a way to overcome. This, in my opinion, is one of the biggest shortcomings of JavaScript that I have encountered to date.

On with the problem...

Say that you have a class that you are creating with JavaScript. You want to ensure that the "private" methods and properties of that class cannot be altered by code running in the same window.

This is a relatively trivial thing to do in javascript using closures:

var MyUnchangeableClass = function(){
   var MyUnchangeableClassImpl = function() {
      var _myPrivate = "Private";

      return {
         getPrivate: function() {
            return _myPrivate;
         }
      };
   };

   var _singletonInstance = new MyUnchangeableClass();

   return {
      getPrivate: function() {
         return _singletonInstance.getPrivate();
      }
   };
}

var unchangeableClass = new MyUnchangeableClass();
alert( unchangeableClass.getPrivate() ); // alerts 'Private'

Using a closure like this creates an interesting scoping situation where the property _myPrivate is locally scoped to the constructor and the 'Object' being returned by the function thus privatizing it both from the global scope and the scope of the closure itself. This scoping situation is called Closure Scope.

This is all fine and great, however, the top level globally scoped reference is always vulnerable to be altered. For instance, we know now that calling getPrivate() on the instantiated closure object will return the innermost value which happens to be "Private", but what happens if I alter the outermost globally accessible reference like this:

unchangeableClass.getPrivate = function() { return "Overwritten"; };
alert( unchangeableClass.getPrivate() ); // alerts 'Overwritten'

We have effectively subverted the entire point of the closure by substituting what the outermost function actually does (coincedentally, the references to anything Closure Scoped will disappear in this case as well, and will be ultimately GC'd, effectively removing the data from the Runtime entirely)

The one thing that is truly holding JavaScript back from being a language that has any hope in a secure world is the fact that there is NO RELIABLE way to SEAL an object. In Java, you can make a class final to ensure that the class cannot be extended to alter it's behavior, you can make properties and methods private to ensure that their behavior and values cannot be altered. There are similar strategies in just about every language that provides OOP or OOP-Like functionality (even PHP!).

I have come up with some somewhat hacky ways to accomplish this, but they can all be subverted by other code running the same runtime. I have even thought about creating a trusted JS plugin for browsers that basically keeps the JavaScript inside a sealed/signed jar after it has been downloaded (so processes outside of the browser cannot modify the cached javascript) and having the initial javascript source tell the plugin which object should be sealed, then have the plugins use the underlying C API to ensure that they cannot be changed. This is a huge undertaking and frankly is open to a ton of potential attacks.

The idea behind the ESAPI4JS library is good - basically providing end users with protection from server side code that has been compromised as a second level of defense. This is something that I think will become increasingly important as time goes on, but without the ability to create secure JS code that cannot be altered, it simply won't live up to it's full potential.

It will provide a second level of defense that can ultimately in most cases still protect the end user, afterall, the code to subvert the server-side code will be the main focus of the nefarious individual doing so, and they may not even realize that there is another layer of protection against potential front-end victims that also needs to be subverted.

I still think it is a great idea, and am excited to see where it goes, but I throw this out to the JavaScript community as both a challenge and a plea. This is something that JavaScript sorely needs (I am sure most JS library writers will agree) and the only way that we will ever see it is if the community demands it.

So Testify! To Securify!

Onward!

2 comments:

  1. Great post Chris, finally a really good concrete example of how JS can never be completely secure. Added to my "in your face" security repertoire

    ReplyDelete
  2. Thanks! It is pretty frustrating trying to provide a secure solution to software development in a language that is used in virtually every environment in nearly every web application when it lacks the most basic of features. This would be a great place for a company in the position of one like google to shine and add this functionality into the Gears toolkit, which unfortunately never took off like I felt it should have. That in itself is indicative of the mindset of the average internet user. I am currently looking into writing a plugin for multiple browsers that provides this functionality but it is proving to be a complex problem to solve because literally every browser would require it's own codebase. At this point ESAPI4JS is likely going to have to turn into ESAPI4Users and become an entire suite of plugins and javascript code - a large undertaking for a couple of guys doing this in their spare time with little if any support from the browser manufacturers (I'm looking at you Microsoft)

    ReplyDelete