Security Engineering · Authorization
Object Capabilities and the Confused Deputy
How unforgeable references replace identity checks, why ambient authority enables the confused deputy, and what membrane and revocation designs cost in a capability system.
A capability is a reference that simultaneously designates an object and conveys authority to use it. Holding the reference is the authorization; there is no separate check to consult, and no name that can be guessed to reach the object without one. Systems built on identity checks ask “who is calling” and then look up permissions. Capability systems ask nothing, because the caller could not have obtained the reference unless someone with authority gave it to them. The distinction sounds like a matter of taste until the confused deputy appears.
The deputy that cannot tell its clients apart
Hardy’s account concerns a compiler that was permitted to write to a system statistics file that ordinary users could not write. Users invoked the compiler with an output path. The compiler, running with its own elevated authority, opened the path and wrote. A user could therefore direct the compiler to write to the statistics file, and the compiler would comply, because it checked that it had write permission on the path and never asked whether the user did. The deputy was not malicious and not buggy in the ordinary sense; it had authority the caller lacked, and it applied that authority to a caller-supplied name.
Every instance of this shape has the same three ingredients: a component with ambient authority, a name supplied by a less privileged party, and an operation that resolves the name using the component’s own credentials. Setuid-root programs, privileged daemons acting on client-supplied file paths, template engines that resolve user-supplied module names, and cloud service roles that act on resource identifiers from a request are all the same structure.
Capabilities remove the middle term. If the compiler receives a capability to an output stream rather than a path string, the caller can only pass a capability they already hold, and the compiler’s authority is never consulted on the caller’s behalf. The deputy can still be confused about intent, but it cannot be tricked into exercising authority it was not handed.
Attenuation instead of delegation
The reason capabilities scale beyond a toy model is that they can be derived. A holder of a capability to a directory can produce a capability to one file within it, or to the directory with read removed and only write retained, without the cooperation of the original granter. The derived capability is strictly weaker. This property is what allows a system to hand a subsystem exactly the rights it needs and nothing more, and it composes: attenuation can be applied repeatedly along a delegation chain, and the resulting authority is the intersection of every restriction applied.
Compare the identity-based equivalent. Delegating a subset of authority requires either a policy entry naming the delegate, which the delegator usually cannot create without administrative rights, or an impersonation token whose scope is defined by the platform rather than by the delegator. The delegator’s actual authority is not a value they can hand over in reduced form; it is a set of rows in a table they do not control.
// Attenuate by wrapping: the wrapper exposes only what it chooses to.
function readOnly(file) {
return {
read: () => file.read(),
// no write, no truncate, no metadata mutation
};
}
// A caller that receives readOnly(f) cannot recover the underlying object
// through any property the wrapper does not expose.
The wrapper works because the capability is the reference. In a language where objects can be reflected upon, or where the underlying file descriptor is reachable through an accessible field, the wrapper leaks the full authority and the attenuation is illusory. Object-capability languages achieve the property by removing reflective access to an object’s internals and by making the only way to affect an object be a message to a reference.
Ambient authority is the thing being removed
Ambient authority is authority a component can exercise merely by existing: a process running as a user can open any file that user can open, by name, without being handed anything. Most operating systems grant authority this way. A process’s identity is a key to a global namespace, and the namespace is the ambient authority.
The capability discipline replaces the global namespace with a graph of references reachable from a root. A process starts with a small set of capabilities, and everything it can reach is reachable through them. There is no open("/etc/passwd") because there is no global namespace to name; there is only the set of directory capabilities the process was given, and each can be asked for a child by name within itself.
Real systems approximate this. File descriptors are capabilities in the narrow sense: an open descriptor conveys authority to the open file, and passing it over a Unix socket with SCM_RIGHTS transfers that authority without a name ever being resolved. The process still has ambient authority over the rest of the filesystem, so the model is not pure, but the descriptor-passing mechanism demonstrates the useful part: authority can travel between processes without either party needing to be able to name the object.
The approximation has a leak that matters. A descriptor can be discovered by a process that shares the descriptor table, which is why fork followed by exec requires close-on-exec discipline. A descriptor number is a small integer, so a confused process can simply try values. Capability systems in the pure sense use references that cannot be enumerated or guessed.
Revocation is the hard problem
Authority that cannot be withdrawn is a liability. Identity-based systems revoke by editing a table, and the next check consults the new state. Capability systems have distributed the authority into references held in unknown places, so revocation requires either indirection or cooperation.
The indirect approach inserts a forwarder between the holder and the object. Every call goes through the forwarder, and revocation severs the forwarder. This works and costs one additional indirection per call, plus a record per outstanding capability. It also changes the identity of the object as seen by the holder, which breaks any code that compares references for equality.
The cooperative approach requires the object to check a revocation list on each invocation, which reintroduces a shared mutable structure and its contention. The caretaker approach restricts the capability’s use to a single trusted mediator that can refuse to forward.
In practice the choice depends on the granularity of revocation required. Revoking a whole subsystem’s authority can be done by dropping the reference graph that leads to it, provided the subsystem’s references are not duplicated elsewhere. Revoking one specific right held by one specific party, while that party holds an attenuated derivation, requires indirection all the way down.
Where the model meets deployed systems
Browser security is the largest deployed capability system in ordinary use. A WindowProxy reference is a capability; the same-origin policy constrains which references can be obtained and what operations they permit, and the postMessage mechanism transfers limited authority between otherwise isolated contexts. The model is incomplete, because a page also has ambient authority over its own origin’s storage and network, but the cross-origin part behaves capably.
Cloud credential systems approximate attenuation with policy documents attached to issued credentials, where the issuing service intersects the requested permissions with the issuer’s own. That is attenuation by construction, and it fails the same way wrappers fail: if the issued credential can be used to obtain a broader one, the chain is broken.
The practical lesson from deployments is that capability discipline pays off most at boundaries where the two sides do not trust each other, and least inside a single trust domain where ambient authority is already uniform. The confused deputy is a boundary phenomenon, which is why the fix belongs at the boundary. The reference monitor framing still applies: a capability system is a monitor whose check is the possession of a reference, and its completeness depends on there being no other route to the object. Related enforcement designs appear in the policy-as-code discussion, where the same question is answered with declarative rules over an ambient namespace.
References
- The Confused Deputy (or why capabilities might have been invented) — Norman Hardy, ACM SIGOPS Operating Systems Review, 1988
- Capability Myths Demolished — Miller, Yee & Shapiro, Johns Hopkins University
- E Language and Capability-Based Security documentation — erights.org