[Python-Dev] Re: Capabilities
Zooko
zooko@zooko.com
Mon, 10 Mar 2003 16:15:18 -0500
(I, Zooko, wrote the lines prepended with "> > ".)
Guido wrote:
>
> > The [Principle-of-Least-Privilege approach to securing a standard library]
> > is to separate the tools so that dangerous ones don't come tied together
> > with common ones. The security policy, then, is expressed by code that
> > grants or withholds capabilities (== references) rather than by code that
> > toggles the "restricted" bit.
>
> This sounds interesting, but I'm not sure I follow it. Can you
> elaborate by giving a couple of examples?
First let me say that "capability access control" [1] is a theoretical
construct, comparable to "access control lists" [2] and "Trust Management" [3].
Each is a formal model for specifying access control rules -- who is allowed to
do what.
But in the context of Python we are interested not only in the theoretical
model but also in a specific way of implementing it -- by making object
references unforgeable and binding all authorities to object references.
So in this discussion it may not be clear whether a claimed advantage of
"capabilities" flows from the formal model or from the practice of unifying
security programming with object oriented programming. I don't think it is
important to differentiate in this discussion.
Now for examples...
Hm, well first of all, where are rexec and Zope proxies currently used?
I believe that a "cap-Python" would support those uses, implementing the same
security policies, but more cleanly since access control would be a first-class
part of the language.
I don't know Zope very well, and rather than guess, I'd like to ask someone who
does know Zope to give a typical example of how proxies are used in workaday
Zope. I suspect that capabilities are quite similar to Zope proxies.
Now for a quick made-up example to demonstrate what I meant about expressing
security policy above, consider a tic-tac-toe game that is supposed to draw to
the screen.
In "restricted Python v1", certain modules have been flagged as "safe" and others
"unsafe". Code can execute other code with a "restricted" flag set, something
like this:
# restricted Python v1
game = eval(TicTacToeGame, restricted=True)
game.display()
Unfortunately, in "restricted Python v1", all of the modules that allow drawing
to the screen are marked as "unsafe", so the tic-tac-toe-game immediately dies
with an exception.
In "restricted Python v2", an arbitrary security policy can be implemented:
# restricted Python v2
games=[]
def securitypolicy(subject, action, object):
if ((subject in games) and (action == "import") and (object == "wxPython")) or
(subject in games) and (action == "execute") and (object == "wxPython.Window") or
(subject in games) and (action == "execute") and (object == "wxPython.Window.paint")):
return True
# ...
return False
game = eval(TicTacToeGame, policy=securitypolicy)
gameobjh.append(game)
game.display()
I think that the "rexec" design was along the lines of "restricted Python v2",
but I apologize if this simple analogy insults anyone.
I'm not sure whether "restricted Python v2" is expressive enough to implement
the capability security access control model or not, but I don't care, because
I don't like "restricted Python v2". I like restricted Python v3:
# restricted Python v3
game = TicTacToeGame()
game.display(wxPython.wxWindow())
Now the game object has a reference to the window object, and it can use that
reference to draw the pictures. If I later change this design and decide that
instead of drawing to a window, I want the game to write to a file, then I'll
change the implementation of the TicTacToeGame class, and then'll I'll come back
here to this code and change it from passing a wxWindows to:
# restricted Python v3
game = TicTacToeGame()
game.display(open("/tmp/tttgame.out","w"))
Now if I were writing in "restricted Python v2", then in addition to those two
changes I would also have to make a third change, which is to edit my
securitypolicy function in order to allow this particular game object to access
a file named "/tmp/tttgame.out", and to disallow it access to wxPython:
# restricted Python v2
def securitypolicy(subject, action, object):
if (subject in games) and (action in ("read", "write",)) and (object == "file:/tmp/tttgame.out"):
return True
# ...
return False
game = TicTacToeGame()
game.display("/tmp/tttgame.out")
This is what I meant by saying that the security policy is expressed in Python
instead of by twiddling access bits in an embedded policy language. In a
capability-secure language, the change (which the programmer has to make anyway),
from "wxPython.wxWindows()" to "open('/tmp/tttgame.out', 'w')" is necessary and
sufficient to enforce the programmer's intended security policy, so there is no
need for the redundant and brittle "policy" function.
I find this unification access control and application logic to resonate deeply
with the Zen of Python.
Regards,
Zooko
[1] http://www.eros-os.org/papers/shap-thesis.ps
[2] http://www.research.microsoft.com/~lampson/09-Protection/Acrobat.pdf
[3] http://citeseer.nj.nec.com/blaze96decentralized.html