Security capabilities in Python

I would like to experiment with security based on Python references as security capabilities. Unfortunatly, there are several problems that make Python references invalid as capabilities: * There is no way to create secure proxies because there are no private attributes. * Lots of Python objects are reachable unnecessarily breaking the principle of least privelege (i.e: object.__subclasses__() etc.) I was wondering if any such effort has already begun or if there are other considerations making Python unusable as a capability platform? (Please cc the reply to my email)

You might take a look at zope.security: http://svn.zope.org/Zope3/trunk/src/zope/security/ It isn't a capability-based system, but it does address similar problems and might have some useful ideas. See the README.txt and untrustedinterpreter.txt. Jim Eyal Lotem wrote:
I would like to experiment with security based on Python references as security capabilities.
Unfortunatly, there are several problems that make Python references invalid as capabilities:
* There is no way to create secure proxies because there are no private attributes. * Lots of Python objects are reachable unnecessarily breaking the principle of least privelege (i.e: object.__subclasses__() etc.)
I was wondering if any such effort has already begun or if there are other considerations making Python unusable as a capability platform?
(Please cc the reply to my email) _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/jim%40zope.com
-- Jim Fulton mailto:jim@zope.com Python Powered! CTO (540) 361-1714 http://www.python.org Zope Corporation http://www.zope.com http://www.zope.org

"Eyal Lotem" <eyal.lotem@gmail.com> wrote in message news:b64f365b0504080701206af8d3@mail.gmail.com...
I would like to experiment with security based on Python references as security capabilities.
I am pretty sure that there was a prolonged discussion on Python, security, and capability on this list a year or two ago. Perhaps you can find it in the summary archives or the archives themselves. tjr

On Fri, 8 Apr 2005, Eyal Lotem wrote:
I would like to experiment with security based on Python references as security capabilities.
This is an interesting and worthwhile thought. Several people (including myself) have talked about the possibility of doing this in the past. I believe the two problems you mention can be addressed without modifying the Python core.
* There is no way to create secure proxies because there are no private attributes.
Attributes are not private, but local variables are. If you use lexical scoping to restrict variable access (as one would in Scheme, E, etc.) you can create secure proxies. See below.
* Lots of Python objects are reachable unnecessarily breaking the principle of least privelege (i.e: object.__subclasses__() etc.)
True. However, Python's restricted execution mode prevents access to these attributes, allowing you to enforce encapsulation. (At least, that is part of the intent of restricted execution mode, though currently we do not make official guarantees about it.) Replacing __builtins__ activates restricted execution mode. Here is a simple facet function. def facet(target, allowed_attrs): class Facet: def __repr__(self): return '<Facet %r on %r>' % (allowed_attrs, target) def __getattr__(self, name): if name in allowed_attrs: return getattr(target, name) raise NameError(name) return Facet() def restrict(): global __builtins__ __builtins__ = __builtins__.__dict__.copy() # Here's an example. list = [1, 2, 3] immutable_facet = facet(list, ['__getitem__', '__len__', '__iter__']) # Here's another example. class Counter: def __init__(self): self.n = 0 def increment(self): self.n += 1 def value(self): return self.n counter = Counter() readonly_facet = facet(counter, ['value']) If i've done this correctly, it should be impossible to alter the contents of the list or the counter, given only the immutable_facet or the readonly_facet, after restrict() has been called. (Try it out and let me know if you can poke holes in it...) The upshot of all this is that i think you can do secure programming in Python if you just use a different style. Unfortunately, this style is incompatible with the way classes are usually written in Python, which means you can't safely use much of the standard library, but i believe the language itself is not fatally flawed. -- ?!ng

On Sat, 9 Apr 2005 00:13:40 -0500 (CDT), Ka-Ping Yee <python-dev@zesty.ca> wrote:
On Fri, 8 Apr 2005, Eyal Lotem wrote:
I would like to experiment with security based on Python references as security capabilities.
This is an interesting and worthwhile thought. Several people (including myself) have talked about the possibility of doing this in the past. I believe the two problems you mention can be addressed without modifying the Python core.
* There is no way to create secure proxies because there are no private attributes.
Attributes are not private, but local variables are. If you use lexical scoping to restrict variable access (as one would in Scheme, E, etc.) you can create secure proxies. See below.
* Lots of Python objects are reachable unnecessarily breaking the principle of least privelege (i.e: object.__subclasses__() etc.)
True. However, Python's restricted execution mode prevents access to these attributes, allowing you to enforce encapsulation. (At least, that is part of the intent of restricted execution mode, though currently we do not make official guarantees about it.) Replacing __builtins__ activates restricted execution mode.
Here is a simple facet function.
def facet(target, allowed_attrs): class Facet: def __repr__(self): return '<Facet %r on %r>' % (allowed_attrs, target) def __getattr__(self, name): if name in allowed_attrs: return getattr(target, name) raise NameError(name) return Facet()
def restrict(): global __builtins__ __builtins__ = __builtins__.__dict__.copy()
# Here's an example.
list = [1, 2, 3] immutable_facet = facet(list, ['__getitem__', '__len__', '__iter__'])
# Here's another example.
class Counter: def __init__(self): self.n = 0
def increment(self): self.n += 1
def value(self): return self.n
counter = Counter() readonly_facet = facet(counter, ['value'])
If i've done this correctly, it should be impossible to alter the contents of the list or the counter, given only the immutable_facet or the readonly_facet, after restrict() has been called.
(Try it out and let me know if you can poke holes in it...)
The upshot of all this is that i think you can do secure programming in Python if you just use a different style. Unfortunately, this style is incompatible with the way classes are usually written in Python, which means you can't safely use much of the standard library, but i believe the language itself is not fatally flawed.
Does using the gc module to bypass this security count? If so: exarkun@boson:~$ python -i facet.py >>> import gc >>> c = readonly_facet.__getattr__.func_closure[1] >>> r = gc.get_referents(c)[0] >>> r.n = 'hax0r3d' >>> readonly_facet.value() 'hax0r3d' >>> This is the easiest way of which I know to bypass the use of cells as a security mechanism. I believe there are other more involved (and fragile, probably) ways, though. Jp

Jp Calderone <exarkun@divmod.com> writes:
Does using the gc module to bypass this security count? If so:
exarkun@boson:~$ python -i facet.py >>> import gc >>> c = readonly_facet.__getattr__.func_closure[1] >>> r = gc.get_referents(c)[0] >>> r.n = 'hax0r3d' >>> readonly_facet.value() 'hax0r3d' >>>
This is the easiest way of which I know to bypass the use of cells as a security mechanism. I believe there are other more involved (and fragile, probably) ways, though.
The funniest I know is part of PyPy: def extract_cell_content(c): """Get the value contained in a CPython 'cell', as read through the func_closure of a function object.""" # yuk! this is all I could come up with that works in Python 2.2 too class X(object): def __eq__(self, other): self.other = other x = X() x_cell, = (lambda: x).func_closure x_cell == c return x.other It would be unfortunate for PyPy (and IMHO, very un-pythonic) if this process became impossible. Cheers, mwh -- Java sucks. [...] Java on TV set top boxes will suck so hard it might well inhale people from off their sofa until their heads get wedged in the card slots. --- Jon Rabone, ucam.chat

On Apr 9, 2005, at 2:13 PM, Michael Hudson wrote:
The funniest I know is part of PyPy:
def extract_cell_content(c): """Get the value contained in a CPython 'cell', as read through the func_closure of a function object.""" # yuk! this is all I could come up with that works in Python 2.2 too class X(object): def __eq__(self, other): self.other = other x = X() x_cell, = (lambda: x).func_closure x_cell == c return x.other
It would be unfortunate for PyPy (and IMHO, very un-pythonic) if this process became impossible.
It would be quite fortunate if you didn't have to do all that, and cell just had a "value" attribute, though. James

James Y Knight <foom@fuhm.net> writes:
On Apr 9, 2005, at 2:13 PM, Michael Hudson wrote:
The funniest I know is part of PyPy:
def extract_cell_content(c): """Get the value contained in a CPython 'cell', as read through the func_closure of a function object.""" # yuk! this is all I could come up with that works in Python 2.2 too class X(object): def __eq__(self, other): self.other = other x = X() x_cell, = (lambda: x).func_closure x_cell == c return x.other
It would be unfortunate for PyPy (and IMHO, very un-pythonic) if this process became impossible.
It would be quite fortunate if you didn't have to do all that, and cell just had a "value" attribute, though.
Indeed. The 2.2 compatibility issue remains, though. Cheers, mwh -- Presumably pronging in the wrong place zogs it. -- Aldabra Stoddart, ucam.chat

On Sat, 9 Apr 2005, Michael Hudson wrote:
The funniest I know is part of PyPy:
def extract_cell_content(c): """Get the value contained in a CPython 'cell', as read through the func_closure of a function object.""" # yuk! this is all I could come up with that works in Python 2.2 too class X(object): def __eq__(self, other): self.other = other x = X() x_cell, = (lambda: x).func_closure x_cell == c return x.other
That's pretty amazing.
It would be unfortunate for PyPy (and IMHO, very un-pythonic) if this process became impossible.
Not a problem. func_closure is already a restricted attribute. IMHO, the clean way to do this is to provide a built-in function to get the cell content in a more direct and reliable way, and then put that in a separate module with other interpreter hacks. That both makes it easier to do stuff like this, and easier to prevent it simply by forbidding import of that module. -- ?!ng

On Sat, 9 Apr 2005, Jp Calderone wrote:
Does using the gc module to bypass this security count? If so:
exarkun@boson:~$ python -i facet.py >>> import gc >>> c = readonly_facet.__getattr__.func_closure[1] >>> r = gc.get_referents(c)[0] >>> r.n = 'hax0r3d' >>> readonly_facet.value() 'hax0r3d' >>>
You can't get func_closure in restricted mode. (Or at least, i can't, using the Python included with Mac OS 10.3.8.) >>> restrict() >>> readonly_facet.__getattr__.func_closure Traceback (most recent call last): File "<stdin>", line 1, in ? RuntimeError: restricted attribute >>> Even though this particular example doesn't work in restricted mode, it's true that the gc module violates capability discipline, and you would have to forbid its import. In any real use case, you would have to restrict imports anyway to prevent access to sys.modules or loading of arbitrary binaries. For a version that restricts imports, see: http://zesty.ca/python/facet.py Let me know if you figure out how to defeat that. (This is a fun exercise, but with a potential purpose -- it would be nice to have a coherent story on this for Python 3000, or maybe even Python 2.x.) -- ?!ng

Ka-Ping Yee wrote:
On Sat, 9 Apr 2005, Jp Calderone wrote:
Does using the gc module to bypass this security count? If so:
exarkun@boson:~$ python -i facet.py
import gc c = readonly_facet.__getattr__.func_closure[1] r = gc.get_referents(c)[0] r.n = 'hax0r3d' readonly_facet.value() 'hax0r3d'
You can't get func_closure in restricted mode. (Or at least, i can't, using the Python included with Mac OS 10.3.8.)
>>> restrict() >>> readonly_facet.__getattr__.func_closure Traceback (most recent call last): File "<stdin>", line 1, in ? RuntimeError: restricted attribute >>>
Even though this particular example doesn't work in restricted mode, it's true that the gc module violates capability discipline, and you would have to forbid its import. In any real use case, you would have to restrict imports anyway to prevent access to sys.modules or loading of arbitrary binaries.
For a version that restricts imports, see:
http://zesty.ca/python/facet.py
Let me know if you figure out how to defeat that.
you should probably search the list and look at my old attacks against restricted execution, there's reason why is not much supported anymore. One can still try to use it but needs to be extremely careful or use C defined proxies... etc.
(This is a fun exercise, but with a potential purpose -- it would be nice to have a coherent story on this for Python 3000, or maybe even Python 2.x.)
-- ?!ng _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/pedronis%40strakt.com

On Apr 9, 2005, at 5:37 PM, Ka-Ping Yee wrote:
Let me know if you figure out how to defeat that.
You can protect against this, too, but it does show that it's *really* hard to get restricting code right...I'm of the opinion that it's not really worth it -- you should just use OS protections. untrusted_module.py: class foostr(str): def __eq__(self, other): return True def have_at_it(immutable_facet, readonly_facet): getattr(immutable_facet, foostr('append'))(5) print immutable_facet James

On Sat, 9 Apr 2005, James Y Knight wrote:
You can protect against this, too, but it does show that it's *really* hard to get restricting code right...
Good point. If you can't trust ==, then you're hosed.
I'm of the opinion that it's not really worth it -- you should just use OS protections.
This i disagree with, however. OS protections are a few orders of magnitude more heavyweight and vastly more error-prone than using a language with simple, clear semantics. Predictable code behaviour is good. -- ?!ng

It may be really hard to get it right, unless we are overlooking some simple solution. I disagree that we should "just use OS protections". The reason I am interested in Pythonic protection is because it is so much more powerful than OS protections. The capability model is much more powerful than the ACL model used by all OS's these days, and allows for interesting security concepts. What about implementing the facet in C? This could avoid the class of problems you have just mentioned. On Apr 9, 2005 2:02 PM, James Y Knight <foom@fuhm.net> wrote:
On Apr 9, 2005, at 5:37 PM, Ka-Ping Yee wrote:
Let me know if you figure out how to defeat that.
You can protect against this, too, but it does show that it's *really* hard to get restricting code right...I'm of the opinion that it's not really worth it -- you should just use OS protections.
untrusted_module.py:
class foostr(str): def __eq__(self, other): return True
def have_at_it(immutable_facet, readonly_facet): getattr(immutable_facet, foostr('append'))(5) print immutable_facet
James

On Sun, 10 Apr 2005, Eyal Lotem wrote:
It may be really hard to get it right, unless we are overlooking some simple solution.
To "get it right", you at least need to know exactly what your operators mean. I messed up because i failed to realize that '==' can be redefined, and 'in' depends on '==' to work properly.
What about implementing the facet in C? This could avoid the class of problems you have just mentioned.
I don't think that's a good solution. A facet is just one basic programming pattern that you can build in a capability system; it would be silly to have to go back to C every time you wanted to build some other construct. A better way would be to start with capabilities that behave simply and correctly; then you can build whatever you want. -- ?!ng

Ka-Ping wrote:
counter = Counter() readonly_facet = facet(counter, ['value'])
If i've done this correctly, it should be impossible to alter the contents of the list or the counter, given only the immutable_facet or the readonly_facet, after restrict() has been called.
I'm probably missing something, but a straightforward reflection approach seems to work on my machine:
restrict() readonly_facet = facet(counter, ['value']) print readonly_facet.value() 0 readonly_facet.value.im_self.n = "oops!" print readonly_facet.value() oops! class mycounter: ... def value(self): return "muhaha!" ... readonly_facet.value.im_self.__class__ = mycounter print readonly_facet.value() muhaha! ... readonly_facet.value.im_func.func_globals["readonly_facet"] = myinstance ...
and so on does that restrict() function really do the right thing, or is my python install broken? </F>

On Sat, 9 Apr 2005, Fredrik Lundh wrote:
Ka-Ping wrote:
counter = Counter() readonly_facet = facet(counter, ['value'])
If i've done this correctly, it should be impossible to alter the contents of the list or the counter, given only the immutable_facet or the readonly_facet, after restrict() has been called.
I'm probably missing something, but a straightforward reflection approach seems to work on my machine:
That's funny. After i called restrict() Python didn't let me get im_self. >>> restrict() >>> readonly_facet.value <bound method Counter.value of <__main__.Counter instance at 0x41df0>> >>> readonly_facet.value.im_self Traceback (most recent call last): File "<stdin>", line 1, in ? RuntimeError: restricted attribute >>> It doesn't matter if i make the facet before or after restrict(). >>> restrict() >>> rf2 = facet(counter, ['value']) >>> rf2.value <bound method Counter.value of <__main__.Counter instance at 0x41df0>> >>> rf2.value.im_self Traceback (most recent call last): File "<stdin>", line 1, in ? RuntimeError: restricted attribute >>> I'm using Python 2.3 (#1, Sep 13 2003, 00:49:11) [GCC 3.3 20030304 (Apple Computer, Inc. build 1495)] on darwin -- ?!ng

Eyal Lotem wrote:
I would like to experiment with security based on Python references as security capabilities.
Unfortunatly, there are several problems that make Python references invalid as capabilities:
* There is no way to create secure proxies because there are no private attributes. * Lots of Python objects are reachable unnecessarily breaking the principle of least privelege (i.e: object.__subclasses__() etc.)
I was wondering if any such effort has already begun or if there are other considerations making Python unusable as a capability platform?
You might want to have a look at mxProxy objects. These were created to provide secure wrappers around Python objects with a well-defined access mechanism, e.g. by defining a list of methods/attributes which can be accessed from the outside or by creating a method which then decides whether access is granted or not: http://www.egenix.com/files/python/mxProxy.html Note that the new-style classes may have introduced some security leaks. If you find any, please let me know. PS: A nice side-effect of the these proxy objects is that you can create weak-reference to all Python objects (not just those that support the protocol). -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Apr 18 2005)
Python/Zope Consulting and Support ... http://www.egenix.com/ mxODBC.Zope.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
::: Try mxODBC.Zope.DA for Windows,Linux,Solaris,FreeBSD for free ! ::::
participants (10)
-
Eyal Lotem
-
Fredrik Lundh
-
James Y Knight
-
Jim Fulton
-
Jp Calderone
-
Ka-Ping Yee
-
M.-A. Lemburg
-
Michael Hudson
-
Samuele Pedroni
-
Terry Reedy