Check out a free preview of the full The Good Parts of JavaScript and the Web course

The "Object Capabilities" Lesson is part of the full, The Good Parts of JavaScript and the Web course featured in this preview video. Here's what you'd learn in this lesson:

Using the principle of least authority, Doug explains how the “actor model” can be applied to object oriented programming to create more secure software. He calls this application Object Capabilities.


Transcript from the "Object Capabilities" Lesson

>> [MUSIC]

>> Douglas Crockford: But there is hope in the principle of least authority. Which teaches that any unit of software should be given just the capability as it needs to do its work and no more.
>> Douglas Crockford: Capabilities can be seen in the Actor model, which was discovered at MIT in 1973.

The investigation of the Actor model led to the invention of Scheme, which led to higher order functions and all that good stuff. It also lead to Actors. So in the Actor model, an actor is a computational entity. It's like asynchronous object systems. An actor can send a message to another actor only if it knows its address.

An actor can create a new actor and an actor can create messages. Web workers are kind of like actors web services aren't but should. There is a system called Waterken that was developed at HP Labs, which applies the after model to web services. I think the stuff is brilliant, it gives you very reliable systems.

I highly recommend getting into that.
>> Douglas Crockford: And so one of the things that comes out of the the Actor model is the idea of capabilities. So an address to an actor is a capability or reference to an object is a capability. So let's apply capabilities to object-oriented programming, looking at object capabilities.

So here is an object. A is an object, it has state and behavior. Object A has a reference to object B because objects can have references. Object A can communicate with Object B because it has that reference. Object B provides an interface that constrains access to its own state in references.

So object A does not get access to B's internals, only to its interface. This is why I like freezing in JavaScript. Because in JavaScript, that is the only way to guarantee this. The only way to make good reliable objects. Object A does not have a reference to C, so A cannot communicate with C.

It's as though there is a firewall between A and C expect that firewall is implemented at zero cost.
>> Douglas Crockford: An object capability system is produced by constraining the ways that references are obtained. A reference cannot be obtained simply by knowing the name of a public of a global variable or a public class.

And in fact, there are exactly three ways to obtain a reference by creation, by construction, and by introduction. By creation means that if a function creates an object it gets a reference to that object. By construction means that an object may be endowed by its constructor with references.

This can include references in the constructors context and inherited references. And then 3, this is the interesting one by introduction. So here A has references to B and C. B and C have no references, so they cannot communicate. But A wants them to be able to communicate. So A communicates with B.

Passing a reference to C, and once that is done, B has now acquired the capability to communicate with C. That's why this is called the capability model. If references can only be obtained by creation, construction, or introduction, then you may have a safe system. And if they can be obtained in any other way, you don't.

So potential weaknesses to watch out for include arrogation, corruption, confusion, and collusion. Arrogation means to take or claim for oneself without right. This include global variables, public static variables, standard libraries that grant powerful capabilities, address generation. Which is why C++ can never be as secure a language and known URLs in web systems.

Corruption, It should not be possible to tamper with or circumvent the system or other objects. Again, that's why the freezing is so critically important. Number 3 confusion, it should be possible to create objects that are not subject to confusion. Because a confused object can be tricked, and then misusing its capabilities.

And then, finally collusion. It must not be possible for two objects to communicate until they are introduced. If two independent objects conclude, they might be able to pool their capabilites to cause harm. Some capabilities are too dangerous to give to guest code. So we can instead give those capabilities to intermediate objects that will constrain their power.

For example, an intermediate object for a file system might limit access to a particular device or directory or so on. Ultimately, every object should be given exactly the capabilities it needs to do its work and no more. So capability should be granted on a need-to-do basis just as we grant information on a need-to-know basis.

The principles of information hiding, which we know leads to good designs and software systems, are enhanced when you're thinking about capability hiding. Intermediate objects or facets can be very lightweight. And class-free languages can be especially effective. I've tried making facets in Java and it turns out, it's really hard because every time you wanna make a little connector, you have to make another class.

And that's a lot of work. Whereas in JavaScript, most of these things are just a simple object or generator and, boom, you're done. So here the Facet object limits the Guest object's access to a powerful object. The Guest object cannot tamper with the Facet to get a direct reference to the dangerous object.

References are not revocable in a capability system, once you introduce an object, you can't ask it to forget it. You can ask but you can not depend on the request being honored. So here the Guest object has a reference to an Agency object. The Guest asks for an introduction to the powerful object,

>> Douglas Crockford: But the Agency instead makes a Facet and gives the Facet to the Guest and the Facet might be a simple pass through. The Guest can then call the Facet, the pass. It will then call the powerful object. When the Agency wants to revoke the capability, it tells the Facet to forget its capability.

The Facet is now useless to the guest. This is what you did yesterday. Yesterday's revocable function was this pattern, so these things are really easy to implement. A Facet can mark requests so that the Powerful object can know where they came from. That gives us accountability. Facets are very expressive, they're easy to construct, they're lightweight.

They provide attenuation or power reduction. They give us revocation, notification, delegation. And it turns out the best object-oriented patterns are also capabilities patterns. Sometimes when you're trying to design a system and you're trying to figure out does this capability go there or there? And sorting those early on in, when you're doing that design a taxonomy is hard.

But if you think about it in terms of capabilities, what is the least amount of power I need to give to this guy in order to have it work correctly? You tend to end up with the correct designs. That thinking about security make system designing easier not harder.

So Facets can reduce the power of dangerous objects. Most code should not be given directly access to dangerous things, for example the browser innerHTML or document.write. Instead of trying to guess if a piece of code can do something bad, we can give it a safe set of capabilities instead.

You know that even if it is bad, it's limited in what it can do. And capabilities can aid in API design.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now