Secure cooperation in javascript

Today's web programming environment suffers from a cooperation problem. One illustration is that if you include a foreign script into your page, you are compromising the security of your page. In other words, it difficult to compose functionality while remaining confident that the different components will not abuse each other.

A solution found in the desktop world is to build a complicated set of restrictions around your code: ACLs, permissions, sandbox rules, checks and policies. I would argue that it is not working great for the desktop and is definitely not practical for the web.

Alternatively, the proponents of capability-based security advocate an approach based on a simpler set of primitives: encapsulating all authority into objects, ensuring that components can keep data and references private, and the ability to start executing some code with a controlled set of references to pre-existing objects (the default being none, by principle of least authority).

Javascript already has a way of keeping private variables (local variables using "var") and doesn't allow references to be forged. One missing element is a mechanism to invoke a function or evaluate a string in a controlled environment. This could include ways to sandbox function calls (including the eval method) and the <script> element.


Function calls could be made in a controlled environment, by introducing a new keyword, similar to the with primitive except that it would hide the rest of the scope chain. The code would look like this:

var myScope = { obj1 = ... };

function executeSensitiveCode() {
   // this function can use obj1 or any other member of myScope
   // this function code cannot see or modify the global scope (the "window" object)
}

sandbox(myScope) { executeSensitiveCode(); }

A sandboxed <script> element would need a new tag name (so that it requires you to specify a scope factory method by default) and would look like this:

<script> function buildMyScope() {    var myScope = { obj1 = ... };    return myScope; } </script>

<sandboxedscript src=".../sensitive.js" sandbox="buildMyScope()" />




Having such primitives for safe javascript cooperation would be a great step forward, but would only be a start. Other improvements would need to appear over time.

First, most sandboxed code will need access to some default and safe primitives such as the String or Array constructors. But in javascript this lets the sandboxed code modifiy these functions, for example hijacking some of their prototype methods. The sandbox should prevent this somehow, thus safeguarding the caller's environment from tampering.

Second, some large objects and APIs such as the DOM are dangerous because they cannot be segmented easily. The problem is illustrated when I want to pass in a DOM element into my sandbox, but doing so in effect passes references to the entire DOM tree. The developer would need some convenient way of building facets to such APIs.

Finally, the sandboxing solution protects the caller from the sandboxed code, but does not help protecting the sandboxed code from the caller. The caller can mess with the environment expected by the invoked code. Solving this scenario would simplify developing components such as the Windows Live Contacts Control, where content returned from a remote location needs to be kept secret from the host page. Currently, this can only be achieved by using iframes and a url hash hack, but I hope that a better solution could be designed.

Update (2007/10/02):

Doug Crockford gave a talk on the topic, which is available for streaming: Gears and the Mashup problem.
He summarizes the limitations of Javascript's security model, then proposes extending the Google Gear WorkerPool concept to provide a sound security model. The WorkerPool would provide isolation between modules and cooperation between modules would be allowed and controlled thru channels.

Update (2008/02/23):

Ben Adida proposed a similar javascript extension, with_cleanslate, which would allow running code in a default and unpowerful scope.
The "with_cleanstate" works for isolation, but is not great for fine-grained control. That is where the "sandbox" approach above would work better: it would give developers control of what APIs, powers or authorities to pass into the sandboxed scope.

Also worth keeping an eye on is the Google Caja project. More to come on this soon (I have a post queued up on the topic).

Posted by Julien on April 04, 2007. Permalink
Comments
comments powered by Disqus

Nice thinking! One way to remedy the DOM issue is by converting all references to DOM nodes into DocumentFragments. At least that would minimize the DOM tree available.

That only leaves us with expando properties :)

Posted by: Mark Wubben at April 5, 2007 03:31 AM
Trackbacks