Of course, in reality, we are never actually controlling the user’s access – instead
we are controlling programs that act on his behalf. Traditionally we have taken
the view that the program itself is trustworthy: that is, that it will faithfully
carry out the wishes of the user who is controlling it. For that reason, traditional
access control has focused on controls based on the identity of the user running
the program.
In practice we use roles rather than users because of messiness in the real
world:
• Sometimes there isn’t really a particular person associated with an activity
we’d like to control: for example, if we are running a nuclear power plant,
the entities we wish to be in control are identified by their role, such as
”engineer in charge” or ”janitor”. It is the role we wish to grant permission
to, not the person.
• People come and go. When somebody starts a job they acquire a new role
(or roles), and when they leave they lose the role.
• People are fallible: it is useful for their own safety to limit their powers to
their current role so that mistakes are less costly.
But increasingly we are moving to a world where the user cannot trust
the program he is running. Even if we look at traditional environments the
devolution of control away from the data centre and professional IT staff and towards the end user himself means there are programs running on peoples’
machines that they have no idea how much to trust, and nor do they have any
way to evaluate those programs.
If we look at the Web, particular the modern trend towards mashups, gadgets
and single-domain3 applications (e.g. Twitter, Flickr, Dopplr) we see this
problem in spades. Applications are webpages, users switch between applications,
including to completely new ones, at the click of a mouse. With mashups
and gadgets the user is not even running a single program but multiple programs
from multiple sources all sharing the same page.
Yet we are still trying to control everything with access controls based on
roles (ACBR)4.
This makes no sense at all. The user has no way to sensibly create roles and
the permissions associated with them. Even if they had, the task of assigning
those roles to the various components of, say, a web page would be impossible.
Of course, I would not be saying all this if I did not think there was a better
way. There is: capabilities.
A capability can be thought of as a “handle” representing some operation on
some object. Possession of the capability is all that is required to perform that
operation. The security of a capability system then boils down to your ability
to control who5 has each capability.
Some examples of capabilities are:
• Read this particular file.
• Write the same file (note that this is a completely different capability from
the read capability).
• Pay money into my bank account.
• Pay money into your bank account.
• Take money out of my bank account.
Each of these capabilities is completely independent of all the others. I
cannot derive a read capability from a write capability, nor vice versa. I cannot
take money out of your bank account just because I can pay it in.
Note, however, that it is possible to derive new capabilities from old ones,
for example:
• Write only well-formed HTML to some file, derived from the capability to
write to the file.
• Write a safe subset of HTML to some file (for example, banning <script>
tags and other unsafe constructs, derived from the HTML capability above.
• Transfer money from my bank account to your bank account, derived from
the capability to take money out of my bank account and the one to put
money into yours.
Note that neither of these can do any more than the capabilities they are
derived from (of course: how could they?) – indeed, in both cases they do less.
In fact, this is generally likely to be the case for derived capabilities, otherwise
why bother to derive them?
In order to turn capabilities into a security system, we need some extra
properties...
• Capabilities are unforgeable – that is, the only way to possess a capability
is to be given it, or to derive it from capabilities you already have.
• All access to resources (that is, anything outside the program itself) is
through capabilities.
In practice, it is usually simplest to build capabilities on traditional objectoriented
programming. In this approach, an object corresponds to a group of
capabilities which operate on some underlying state (in the case of read access to
a file, the object might let me do seek operations as well as read operations on the
underlying file object), often itself represented as another object which might be
shared between multiple capabilities (my bank account would be shared between
the capability to deposit money in it and the capability to withdraw money from
it). Deriving new capabilities from old then becomes business as usual – create
a new object, include references to the relevant existing capabilities and away
you go.
When capabilities are implemented in this way you need to impose a further
property on the objects: encapsulation. That is, it should not be possible for
the holder of a capability to look inside it and see its inner state6.
The requirement for unforgeability of course needs to cover naughtiness on
the part of programs: they should not be able to steal capabilities from other
programs, for example, nor should they be able to access an existing capability
by creating a new reference to it.
I hope, by this point, you can see that capabilities could, indeed, form the
basis for a very fine-grained security system. But what do they have to do with
mashups and gadgets? And how on Earth do you manage all these capabilities?
No comments:
Post a Comment