Comparison between Object Capabilities and Information Flow
Augmenting the Capability Model with Information Flows
I’ve already convinced myself that labels are best implemented as tags on primitive values and references. In JavaScript, it is sometimes useful to view an object as a heterogeneous hash table, mapping field names to data. Having to provide a label for the object itself, rather than a label on each datum, necessarily leads to a conservative labeling. This strategy necessarily leads to a malignant amount of label creep.
Given that the mechanism for labeling primitive values naturally accommodates the tagging of references (since references are implemented as a special case of primitive value) we can easily forgo labeling of objects and the resultant complications. By tagging references rather than the objects themselves, we can more precisely track the path of access to a piece of data and avoid the label creep that results from conservative analysis. Of course, tagging references comes at a cost:
- Every reference (and primitive value) increases in size. The exact amount depends on the implementation, but moving to a 128bit internal value is conceivable. (that’s 64bits for a label pointer, 64 bits for the original encoded value)
- Labels will have to be unioned, at runtime, at every step along a reference chain. This implies that label union operations will need to be quick. Offhand, I would expect most of these operations to be useless (unioning a sublabel into an existing label).
The most interesting feature about labeling the references comes to light when we compare this form of the labeling model to the object capabilities model. Under the ocaps model, we find some key properties (blatantly stolen from [1]):
- Delegation. An object has the authority to message another object, if it has a reference to that other object. In general, this represents an excellent approach to security. I see only one drawback: I, as a system designer, would like to be guaranteed, statically at compile time, that some (untrusted) objects will not be able to obtain, by accident or otherwise, references to certain other (trusted) objects, at run time. I believe that labeled references will be able to patch up this gap in knowledge, by enforcing a run-time check on the use of all references. Messages that untrusted objects make to trusted ones can be detected when they occur, as the trust relationship will be encoded in the reference path actually used to send the message, and monitored, at each step, by the VM. (How does this formally compare to a reference monitor?)
In [2], delegation is addressed by extending the core language with an expression “
let(e1 ≤ e2) in e3
“. Scoping becomes critical here: the delegation is allowed (explicitly, in code) only for the operations that occur ine3
, the original hierarchy is restored after those operations complete. - Dynamic Subject Creation. Within the ocaps model, each object is a subject, so the granularity with which authority can be delegated is very fine. Conventional approaches to info flow resemble the Access Control List (ACL), in that the principal actors are first specified in a static hierarchy, and usually represent a much coarser model (at the level of users). Addressing this issue for info flow, requires a mechanism for the creation of new subjects. The delegating
let
mentioned above is not necessarily sufficient, for that form only specifies thate1
ande2
both evaluate to principals. An additional mechanism is needed for instantiating a run-time principal. Such flexibility implies that we shall need first-class principals (and further, first-class labels) in our implementation.The introduction of first-class principals and labels exposes the principal hierarchy to the running program. An extensive analysis should be done on what operations (introduction of new principals, deletion of existing principals, addition of actsFor edges) can be allowed and under what circumstances. The last thing we want from this exposure is to allow the principal hierarchy to become a communication channel in its own right. I believe [2] does such an analysis for its proposed lambda calculus, but I glossed over the technique. Offhand, I would guess that introducing a new principal (using an unforgeable uuid) could be safely done at any time, possessing a principal’s id and satisfying the actsFor check ought to be enough for deletion (caution: what if it is still active elsewhere in the system?, perhaps garbage-collection is the better strategy), and the addition of an actsFor edge is already
discussed in [2].Presupposing the addition of a language mechanism for instantiating a new principal, I’d like to be able to argue (via direct code demonstration) that the introduction of new principals which possess a strict subset of one’s own responsibilities is enough to fully emulate the ocaps model. Comparing each model reveals an interesting difference in approach: The ocaps model uses membranes and proxy objects to delegate and divide authority amongst a spawn of subjects, while the info flow model uses the linguistic concepts of scope and stack. I think there might be a critical separation when it comes to revocation: the ocap model supports revocation at any time (by toggling a proxy forwarding object). I haven’t seen info flow literature discuss revocation, so I’m not sure how to do revocation via the info flow principal hierarchy. However, it naturally occurs when the above
let
has finished execution. - Subject Aggregated Authority Management. In both the ocaps and decentralized information flow models, subjects have the ability to edit only their own attributes. This differs from an ACL, where the ability to edit permissions is typically also conflated with the ability to edit the entire ACL. The decentralized nature of the info flow model really helps in defusing problem with unwarranted privilege escalation. We should still beware though, that allowing first-class principals and the ability to add/remove actsFor relations does not accidentally revive the problem.
One aspect of capability leakage should be addressed now: is it possible for a capability to leak from one domain to another via a data channel? That is, if it is possible for the system to pass around, read, write, and execute capabilities, then does it follow that objects can transmit capabilities anywhere they can transmit data? [1] points out that in type-enforced or partitioned ocaps models, this leakage cannot occur. Given that the problem can be solved when cast as a type-enforcement issue, I see no reason why info flow wouldn’t be able to cope. Although the client programmer should be allowed to create new first-class principal objects (representing capabilities), I see no reason why this implies that said objects should be serializable into a data stream. A fully encapsulated unique identification scheme can hide this ability from the programmer, while still maintaining the unforgeablility requirement.
- Ambient Authority. The ocaps model requires subjects to present the a form of authentication before exercising their authority. This requirement is phrased in terms of capability possession: Authorization to access is granted via a capability delegation, so the very possession of a capability implies the subjects right to access the corresponding resource (via a method invocation). Within the info flow model, we have a different view of the world: the right to access a resource can be delegated in similar fashion (do we need strong code discipline to enforce this point?), but the subject’s right to access is additionally dependent on the context in which the resource is used. This additional restriction takes the form of a runtime check on the current program counter label, preventing implicit information leakage. In this manner it seems possible to reject access that would otherwise be granted under an ACL model. For example, if a nefarious program ‘guessed’ that it should have read access to a global object, this access is potentially deniable based on the fact that the program counter reveals the access takes place from within untrusted code. (perhaps code discipline isn’t needed after all)
- Composability of Authority. Within ocaps, we see that because resources are representable by subjects, there is no separation between the two. As a result, the system is unified under a single abstraction: subjects can be resources, resources can be subjects. Nothing within the info flow model prevents this organization, so it too can benefit from the composibility. JavaScript in particular encourages this organization with its prototype and object model. The only distinction to worry about is the difference between primitives and objects. But by using fat values, we can again achieve the desired uniformity: each primitive has attached a label describing the capabilities (in terms of confidence and integrity labels) invokable on the data. Object references are just another primitive, and will naturally comply with the existing design (the important difference being that references also hold a de-ref capability).
- Access-Controlled Delegation Channels. Under the ocaps model, the access to a resource can only be acquired via (1) initial object creation or (2) passed via a communication channel. This means that communication an access right requires a prior link between objects. Under the iflow model, each reference (to object or resource) carries the context (program counter) in which it was manufactured. Passing around these references can lead to further security restrictions, as the security label on the reference might be upgraded to reflect the context under which it was copied. Access to any global data will also have to follow a path of references, with a label union at each step. Although it’s possible to pass a reference through a shared object (use that object as a data channel) the label on the passed reference will encode this activity. So, despite the communication of references through a data channel, the acquired reference might still incur a security violation at the time of use. (Proof needed: this is enough security, we don’t require the access-controlled delegation)
- Dynamic Resource Creation. Because the ocaps model has object level granularity, it can dynamically create partitions of existing capabilities. In the info flow model, the ability to create new partitions (and combinations) of existing capabilities would incur changes to the principal hierarchy, and the introduction of a new label into the label lattice. As long as these modifications comply with their own set of security restrictions, the info flow model also allows dynamic resource creation.
It’s clear that JS allows programming within the ocap model, as long as code discipline is enforced (say via a source-to-source translation like caja). However, the objectives of each security model are fundamentally different: ocaps seeks to give the programmer flexibility to enforce somewhat arbitrary security policies. Mainly these are aimed at restricting access to certain objects (such as global objects). While info flow seeks to prevent a very specific attack, information leakage.
Ocaps has a fundamental problem with guarantees of the enforced security. Because the security enforcement is based on object accessibility, it can only be checked by walking the run-time reference graph. The composibility principal above helps to make an argument that the graph can only evolve in secure steps, but this proof is not sufficient, because it is easy to forget about a corner-case syntactic form that bypasses the composibility. Info flow can mitigate this problem, because it will offer a guarantee that no resource is used in a disallowed manner. So even if access to information is both obtainable and manipulable it will not be able to leave the system because of a runtime check on external (network) communications. The only channels that would be difficult to protect would be those in native objects, everything JS-only is kept safe.
- References
- [1] Capability Myths Demolished
- [2] Run-time Principals in Information-flow Type Systems