Updated 28 Oct 2015 to clarify a bit, and to elaborate on what choosers provide.
Every application platform needs to decide on 1 or more protection mechanisms to protect principals from each other — and thereby to protect the assets that the principals operate on. (If you are wondering what those words mean, start by reading the classic “The Protection Of Information In Computer Systems” by Saltzer and Schroeder.)
Of course, platforms don’t need to provide a protection mechanism; I was being a bit hyperbolic in the previous paragraph. For example, DOS and Mac OS Classic had no protection mechanisms. But, well, they’re dead, aren’t they? It’s no coincidence: they are dead in part due to their lack of protection.
So, platforms need to provide some protection mechanism to be viable on the modern internet. This includes the Open Web Platform (OWP). As a Chrome security engineer, I am obviously interested in what protection mechanisms the OWP provides; as a Chrome secure usability engineer, I am interested in how well people can understand and use those protection mechanisms, and how we can improve their effectiveness and usability.
Most platforms, including the OWP, use a hybrid design in which more than 1 protection mechanism is at play. As we’ll see, there are reasons for this. However, the more mechanisms there are, the more complicated their interaction and the more complicated the overall system. That complexity makes it more difficult it is for the system’s designers, developers, and users to build accurate mental models of the system.
Thus, we should like to have a ‘pure’ system: a system that uses a single, solid, understandable protection mechanism. When that is not possible, the system should strive to make 1 of the mechanisms primary, and use the other mechanism(s) to bootstrap the primary mechanism.
In the context of the OWP, we want to understand what we call ‘permissions’: special powers that the person browsing a web origin gives to that origin, such as the ability to determine the person’s computer’s geolocation, to read and upload a file from the computer’s local storage, to listen on the computer’s microphone, and so on.
A permission is a (possibly persistent, possibly ephemeral)
capability grant. Think of a
Permission as a hypothetical abstract
superclass that the hypothetical subclasses
SymbolicCapability implement. I’ll try to explain what I mean by
that in the rest of this post.
I use the term capability in the sense of a capability-based protection mechanism (as opposed to an access control list-based protection mechanism). (In other contexts ‘capability’ can mean other things — naming things remains a Hard Problem in computing science.)
The key features of capabilities are:
In some systems, that is not true, or sometimes not true. Windows includes an option to re-check that the bearer of a capability is still authorized on each access. This setting is almost always disabled — except in Chrome’s sandboxed renderer processes! Weird but true.
By contrast, access control list-based mechanisms have only
unforgeability. As an example, consider the POSIX file permissions model (which
is relatively simple, compared to e.g. Windows). For each file, there are 3
principals: the user-owner, the group-owner, and ‘other’ (all other principals).
Each principal has 0 or more of 3 access rights on the file: read, write, or
execute. Whenever a process tries to
open the file, the operating
system kernel checks the process’ principal (user ID and group ID) against the
file’s access control list.
$ ls -l /Music/Thelonious\ Monk/Brilliant\ Corners/ total 48392 -rwxr-xr-x 1 chris chris 8392333 Sep 8 2009 01 Brilliant Corners.mp3 -rwxr-xr-x 1 chris chris 15283234 Sep 8 2009 02 Ba-Lue Bolivar Ba-Lues-Are.mp3 -rwxr-xr-x 1 chris chris 10443816 Sep 8 2009 03 Pannonica.mp3 -rwxr-xr-x 1 chris chris 7029272 Sep 8 2009 04 I Surrender, Dear.mp3 -rwxr-xr-x 1 chris chris 8395820 Sep 8 2009 05 Bemsha Swing.mp3
The user chris can read, write, and execute these MP3 files; any user in the chris group can read or execute them; and any other user can read or execute them.
User chris can change the permissions:
$ chmod -x /Music/Thelonious\ Monk/Brilliant\ Corners/* $ ls -l /Music/Thelonious\ Monk/Brilliant\ Corners/ total 48392 -rw-r--r-- 1 chris chris 8392333 Sep 8 2009 01 Brilliant Corners.mp3 -rw-r--r-- 1 chris chris 15283234 Sep 8 2009 02 Ba-Lue Bolivar Ba-Lues-Are.mp3 ...
including granting write access to ‘other’:
$ chmod o+w /Music/Thelonious\ Monk/Brilliant\ Corners/* $ ls -l /Music/Thelonious\ Monk/Brilliant\ Corners/ total 48392 -rwxr-xrwx 1 chris chris 8392333 Sep 8 2009 01 Brilliant Corners.mp3 -rwxr-xrwx 1 chris chris 15283234 Sep 8 2009 02 Ba-Lue Bolivar Ba-Lues-Are.mp3 ...
which is kind of like transferability. But it’s better to read transferability as meaning “transference to a specific process or principal”. And we can see that this is not possible with POSIX file permissions:
$ grep nobody /etc/passwd nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin $ chown nobody /Music/Thelonious\ Monk/Brilliant\ Corners/* chown: changing ownership of ‘/Music/Thelonious Monk/Brilliant Corners/01 Brilliant Corners.mp3’: Operation not permitted chown: changing ownership of ‘/Music/Thelonious Monk/Brilliant Corners/02 Ba-Lue Bolivar Ba-Lues-Are.mp3’: Operation not permitted ...
A good example of transferability is sending
a file descriptor from 1 process to another over a UNIX domain socket using
On the web platform we would like to use a capability-based protection mechanism to the greatest extent possible, rather than an ACL-based mechanism. There are a few reasons for this.
Capabilities are a good fit for a well-known secure UX pattern: the chooser. An untrustworthy process (like a web renderer) can ask a trustworthy process (like the browser) for a capability (say, a file descriptor from which to read data to upload to the web origin). Only the trustworthy process can grant it. The trustworthy process shows the person a chooser UX, the person selects a file (or, no file), and the trustworthy process opens the file and passes the descriptor to the untrustworthy process. The untrustworthy process has no ambient authority, and the user gets an understandable, empowering interface to selectively grant authority.
Choosers can let people choose much more than just files and directories. A chooser can return any kind of object. Everything that a computer program can represent can be handled as an object capability: any kind of securable object that the operating system can process; any kind of function, object, closure, or continuation.
Additionally, choosers can grant specific objects at a specific time in a
specific context. For example, maybe you would like to grant hangouts.google.com
access to the external USB camera just this once, not all the time. Choosers
make that easy; by contrast, setting an ACL on
/dev/camera may turn
out to be a surprisingly broad grant.
ACLs are a pain in the ass (a) generally; (b) especially when the principals are complex (as web origins + Chrome profiles + people are); and (c) when the principals are numerous (how many web origins do you visit? 90 billion per day).
The ambient namespace that most ACL-based mechanisms provide (e.g. the filesystem, the namespace of named pipes on Windows, et c.) often turns out to provide more information and authority to untrustworthy processes than the person intended. For example, the pathnames themselves might give away information, ACLs have a notable tendency to diverge from the intended permission grant (because they impose a maintenance burden), et c.
Closing the gaps left open in ACL-based systems has proved difficult and inelegant. (See e.g. Windows integrity levels, or SELinux.)
“Applications can be designed to run at a low integrity level. Protected Mode Internet Explorer is an example of a Windows Vista application that is designed to run at low integrity. Applications at low integrity might need folders in the file system where they can write temporary files or application data. In the case of Protected Mode Internet Explorer, the Temporary Internet File folder in the user’s profile is used. How can a low application write to a file system folder? The folder must be assigned an explicit mandatory label that permits write access from a low integrity process.” — MSDN, “Windows Integrity Mechanism Design”
ACLs (especially highly complex ACLs like in Windows) have proved to be hard to use. By contrast, choosers are so easy to use they are now nearly invisible — even engineers often don’t realize how large of a problem they handily solve.
Normally, “capability” means object capability (such as a POSIX file
descriptor, a Windows
HANDLE, a Python object reference, et c.).
However, in the context of the web platform, we often need to persist
capabilities across instantiations of the principal. That is, different renderer
processes running the origin (https, www.example.com, 443) on behalf of profile
1 owned by firstname.lastname@example.org might all need to get a reference to the
same capability, even across browser restart or computer reboot.
For persistent capabilities, we need to persist a symbolic representation: a symbolic capability instead of an object cap. (This is a limitation of the memory persistence mechanisms in common operating systems, not of objects caps themselves; see below.) There must be a way to bootstrap the symbolic cap into an object cap. 1 way to do this is with an ACL, although ACLs are not the only way to achieve this.
Cryptographic capabilities are another form of symbolic cap that a process can bootstrap into an object cap; for example, consider encrypted and integrity protected HTTP cookies that store the session state. In effect, this cookie is an opaque, transferable reference to the next continuation in the session — a persistent capability that requires no ACL to bootstrap. — Wikipedia, “Capability-based security”
In Chrome, we persist capabilities as symbolic caps coupled with a simple form of ACL. In this picture, the Hostname Pattern names a (group of) origins, and the Behavior describes the access grant.
Finally, if we had operating
systems that could persist the live object graphs of processes, we wouldn’t
need the bootstrapping step, and we could have all object caps all the time.
When booting up, the operating system would resuscitate a live object graph from
non-volatile storage into working memory, and all object capabilities would be
live. That would be nice and fancy, but it’s not strictly necessary. ACLs can be
a perfectly good way to bootstrap caps — if and only if bootstrapping is the
only action that uses ACLs. (Consider the design of POSIX, which has both
unlink but not
funlink, and so
on. It would be better — “more cappy” — if POSIX provided only
funlink, and so on.) For
example, all sorts of time-of-check/time-of-use
(TOCTOU) and other vulnerable race conditions are a direct result of operations
on file pathnames (symbolic capabilities) rather than on file descriptors (live
Hybrid ACL/cap platforms should strive to use only object capabilities wherever possible. Where persistence is necessary, we should persist symbolic capabilities and ACLs which we use only to bootstrap live object caps.
I’ve got a draft half-written that talks about a few other protection mechanisms, and their engineering trade-offs.