private "special" methods can be (indirectly) accessed by untrusted code
Opened this issue · 1 comments
The code below constructs a class Protected
with private keys
and __getitem__
methods and shows how to call these methods indirectly from untrusted code:
from AccessControl.tests.testZopeGuards import TestActualPython
t = TestActualPython("testPython")
from AccessControl.class_init import InitializeClass
from AccessControl.SecurityInfo import ClassSecurityInfo
from ExtensionClass import Base
class Protected(Base):
security = ClassSecurityInfo()
@security.private
def keys(self):
return (1, 2, 3)
@security.private
def __getitem__(self, key):
return key
InitializeClass(Protected)
protected = Protected()
access_code="""\
d = {}
d.update(protected)
assert d == {1:1, 2:2, 3:3}
"""
code, g = t._compile_str(access_code, __name__)
g["protected"] = protected
exec(code, g)
The same likely applies to many "special" methods called implicitly by Python (such as __bool__
, __index__
, __iter__
, ...) or methods of Python types. The example above uses dict.update
; similar unprotected access is likely possible with list.extend
(using __iter__
without protection).
Trying to construct examples for a practical security preach I found hints that the problem might have limited practical importance. I concentrated my research on dict.update
and list.extend
; dict
and list
are examples of types which can be used in restricted contexts with almost no restrictions. dict.update
can call its argument's keys+__getitem__
or __iter__
without the security checks normally applied in restricted contexts; list.extend
can do this with __iter__
. In both cases, the result of the unprotected calls arrive in a container (dict
or list
); access to individual elements of such containers is usually protected and typically will fail unless the accessed element is simple (i.e. of an "allowed type") or public.
This means that a security preach could involve an object maintaining sensitive information accessed via "special" methods (e.g. "keys", "getitem", "iter") which essentially only use (potentially recursively) "simple" types/containers.
A constructed example could be a class providing a mapping interface to a user base where each user is described by a dict
or public class instance.