ECMAScript, Mashups, and Security

A mashup composes a web application by combining programs and data services from multiple originators, and delivers it for presentation on a single web page. Usually, these programs and services interact with each other - housing maps being the canonical example.

 

The same origin policy (SOP) governs access control in today’s browsers. It prevents documents or scripts loaded from one origin from getting or setting properties of documents from a different origin - it provides total isolation. While this is useful to protect one origin from another, it shackles mashups. A common technique mashup authors have used to break free is by clever use of the script tag. The SOP is not applicable to the src attribute on the <script> tag; a document may contain <script> elements from different origins (or domains). Such third party scripts are treated as originating from the document’s origin and can access all of the document’s resources - they have total trust.

 

Faced with this all-or-nothing security model, the mashup developer can use the <iframe> structure to confine components such that they cannot interfere with each other, but the SOP isolates them so completely that they are also unable to cooperate, or, use the <script> tag and ECMAScript to enable cross domain data exchange and compose rich client-side mashups.

 

ECMAScript, thus, finds itself being used as a composition language - composing separately written programs into a single web page. Such composition presents several challenges, one of them being the ability to compose securely. When separately written programs are composed so that they may cooperate, how do you prevent them from interfering in unanticipated ways? Does ECMAScript, as a language, support secure software composition? Several features in the language would suggest otherwise. Here are a few:

 

Global scope: All script code executing within the same web page (or <iframe>) implicitly share access to the same global scope. Therefore, globally reachable objects cannot be isolated from each other.

 

Freely mutable state: Certain objects, like Array.prototype, are implicitly reachable even without naming any global variable names. Even if the global scope problem is addressed, the mutability of these objects prevent isolation.

 

Freely mutable properties: While some built-in properties are constrained to be Internal, ReadOnly, DontEnum, or DontDelete, there is no way to express these constraints in the language. Any object defined in user script code is freely mutated by any other code which can access it.

 

Lack of encapsulation: Objects need to be able to encapsulate their private state. The best one can do is to use an idiomatic functional style to simulate private members as well as private static members. But it is idiomatic; neither widely taught nor uniformly followed.

 

for..in loop: The for..in loop enumerates the names of all of an object's properties, whether inherited or not, unless the property has the DontEnum attribute (which the programmer has no way to express). When composing using mixins, programmers must carefully skip over some of these names. Re-inventions of idiomatic mechanisms to achieve this abound.

 

"this" binding: The rules for binding “this" depend on whether a function is invoked by construction, by method call, by function call, or by reflection. If a function written to be called in one way is instead called in another way, its “this" might be re-bound to a different object or even to the global scope.

 

eval: There is no simple way to constrain the script or to inspect it before it executes. It runs with the same authority as scripts from the page.

 

with: with augments the scope chain causing some confusion to the reader about the specific object to which a particular property access gets bound. A bigger problem is probably the with-like semantics implemented by Internet Explorer and Firefox for the exception object within a catch clause of an exception handler. This semantics allows malicious code to inject names into the dynamic scope of a theoretically secure handler. This construct, along with eval, calls for studied usage.

 

Mashups are not new. The early and simple mashups merely loaded an image from a different site, while the contemporary and more profitable ones pop up relevant advertisements as you browse. Rich media ads are mashups. Most rich ads are served from third-party sites into the context of the existing experience. The ad script has access to all of the information on the page, including cookies and the connection to the originating server. Whenever you include any script on your page, you are implicitly trusting that script will neither interfere, maliciously or otherwise, nor destabilize your page. After all, a poorly coded ad can easily bring down the entire application. Security, then, is critical to the growth and success of mashups.

 

Within the Web community there are various initiatives that attempt to address this challenge. Facebook with FBML and FBJS attempts to constrain the language and DOM semantics to protect their users. Adsafe and Caja propose a subset of the language and corresponding semantics that can be validated. Many gadget approaches attempt to use IFrames to provide isolation.

 

Such initiatives are valuable because they are exploring the solution space of the mashup security problem. However, currently they only provide local and limited solutions to the problem. In the long run the best solution is to provide common ubiquitous support for secure mashups within all browsers. This is the realm of web standards, and mashup security should be a high priority item for future revisions.

 

In the meantime, I am interested in knowing what kind of composition challenges you have faced with JScript and mashups, and how you have addressed each of them.

 

Pratap Lakshman, JScript