This entry is part 10 of 12 in the series Server-Side JavaScript, Pintura, and Persevere 2.0

The object capability model is an approach to security that utilizes object references as the primary means of controlling access and providing authority. Capability-based security follows the principle of using unforgeable capabilities to provide access to resources. Object capability builds on capability-based security by leveraging object references as the primary representation of capabilities, which are naturally unforgeable in memory safe languages. Object capability based security is an elegant approach to security because the goals of object-oriented principles of encapsulation and information hiding are realized in virtually the same exact manner as the principle of least authority that is at the heart of object capability security. This type of security is extremely flexible and customizable since it is based on object-oriented design. Plus, writing good code naturally leads to secure code, security can be designed with object encapsulation hand-in-hand.

In the object capability model, the authority to act on an object is permitted when one attains a reference to that object. Primarily, object references are gained by creating a new object (parenthood) or being passed an object reference (introduction). This means that a separate access control system is not needed, the passing of object references to functions and other objects provides the minimal, but sufficient, means and authority to carry out operations.

Object capability provides a fundamentally superior approach to alternates that have led to a vast number of the security flaws that have plagued operating systems and the web. Operating systems have long been unable to provide any true protection against malicious code. This is because access is defined by the current user’s access levels (ACL approach) instead of through capabilities being appropriately passed to programs. In object capability terminology, providing all executing code (for a given user or other context) with the same level of access is known as “ambient authority”. This has essentially enabled the whole class of exploits known as viruses. On the web, the problem is equally severe. Perhaps the most broad security threat to web applications is CSRF. This is also a direct result of how applications use cookies for ambient authority. Cookies are uniformly attached to all requests to a domain, regardless of who or what triggered the request. This allows malicious sites to trigger requests under the ambient authority of a logged in user.

One of the primary goals in the evolution of JavaScript is improved security. A large amount of the revisions in EcmaScript 5, and proposed ideas in the next version, are the result of object capability model research and how it can be applied to JavaScript to avoid ambient authority exploits. In particular, ES5′s strict mode is specifically designed to enable a new class of sandboxing techniques for executing untrusted code. Caja, FBJS, Jacaranda, dojox.secure, and ADsafe are all technologies that employ the object capability model to safely execute suspicious code.

Challenges: Doing something useful

While the object capability model provides a well-grounded and principled approach to security, it is not necessarily obvious, nor intuitive how to build real applications with this model. Application requirements are not usually defined in terms of object references, but rather in terms of who can do what, and how access is controlled. Since it is easy to just default to ACL-style approaches, how do we translate such requirements into object capabilities instead? We will take a look at how Persevere 2.0′s Pintura and Perstore provide a framework for doing just this.

Object capability model advocates tend to be cautious around user-authentication schemes, since they often translate into ambient authority. However, that does not need to be the case, and the reality is that the vast majority of applications will indeed utilize user authentication to control access to resources. This does invalidate the object capability approach. Pintura makes it very easy to use user authentication. Pintura employs a middleware module that reads user credentials, checks them against a data store for credentials (which can be configured to be any data store), and then calls a function that can be implemented by the application to return a set of “capabilities”, which are a set of references to data interfaces that they can access. These capabilities are passed to the entry REST or RPC handler, but one does not need to rely on ambient authority, code can then explicitly pass appropriate capabilities to any other functions that are executed.

Let’s take a look at the Pintura’s example application to see this in action. The app.js module defines a getDataModel function on the security object that implements the logic of determining which capabilities are designated for each user. In this example, we define administrative capabilities, authenticated user capabilities for editing wiki pages, and the capabilities of an unauthenticated user which include the ability to view pages, authenticate and register/sign-up. As your application grows, you may wish to separate out the security logic into a distinct module.

Facets

Looking at the app.js module you will see that most of the capabilities refer to facets. Facets are an integral part of the Pintura/Perstore (Perstore is the persistence/data modeling framework used by Pintura) security system, and are introduced in the getting started with Pintura article. One of the primary challenges of using the object capability model is that a simple reference to an object often falls far short in terms of access level granularity. If you don’t have a reference to an object, you can’t do anything with it, and if you do have a reference, you can do anything to that object. In reality, applications often need much more fine-grained access levels like read-only access, create-only access, ability to delete from collections and even ability to define these levels at individual property levels instead of just the object level. With the object capability model, we can achieve this level of granularity by creating proxy objects that wrap an object. The proxy object can then be referenced and the proxy can handle attempts to modify and/or read data from the source object and determine which operations to allow, and which to deny.

Building these proxy objects can be onerous, but Perstore makes this easy, providing a complete, robust system for controlling access to store model persistent objects with “facets”. We use the term “facet” in Perstore because proxy is an overloaded term, and facets also indicates the secondary purpose of providing alternate views and interfaces of data. The facets in Perstore automate most of the proxying process, and allow a great deal of flexibility in defining access. Facets actually are very analogous to the concept of a proxy server, and follows the layering principle of REST. Like proxies, facets add functionality (attenuation of access), but presents the same interface to the next level up.

As mentioned in the getting started guide, Perstore provides two facet constructors: Restrictive and Permissive. The Permissive constructor gives full access to the underlying objects by default, and you must explicitly define functions or schema constraints to restrict functionality. The Restrictive constructor restricts access to the underlying objects to read-only by default, and you must override functions to permit access. Perstore facets utilize JSON schemas for further defining the facets. The same familiar structure that is used for model object validation is used for facet definitions. Therefore you can write:

var Permissive = require("perstore/facet").Permissive;
var SomeProductFacet = new Permissive(Product, {
  properties:{
    productCode: {readonly: true},
    managerNotes: {blocked: true}
  },
  "delete": function(){
    throw new Error("Can not delete");
  }
}

In this example, we have defined a facet for accessing the Product model and its instances. All instances that are retrieved through SomeProductFacet will be writable objects except that productCode will be read-only and managerNotes will be completely blocked (can not read or write to it). Furthermore, this facet defines that any attempt to delete products through SomeProductFacet will be denied.

Facets are the primary mechanism for authorization in Pintura. This fulfills the essential role in a web application of controlling access of clients. However, with the firm basis on object capability model, Perstore’s faceting will be appropriate for the eventual sandboxing of untrusted code. While JavaScript sandboxing is still somewhat of a research and development level topic, and is more of a platform level concern, (Perstore/Pintura do not provide any sandboxing themselves) such mechanisms are in the works, and should work perfectly with facets to provide fine grained access to persisted data for various modules.

Facets are a powerful concept with a multitude of uses. We have seen how to utilize them for access control, but facets can also play an important role in providing different “views” of data. One can utilize facets for applying different query filters, providing different locale-specific data to achieve internationalization, or revealing various levels of object details based on application views. Out of the box, facets are based on authentication information, but facets (and even layers of facets) can also be selected based on locale, user agent, or custom headers.

Conclusion

Persevere 2.0′s new object capability model with facets provides a flexible security framework based on the best principles of security research, avoiding the pitfalls of ambient authority and giving you the tools to quickly and efficiently build solid, secure applications.