BTW, it's perfectly possible to eliminate open() and file() from usability. The only problem is it requires a separate instance of __bulitins__ (therefore probably a separate interpreter)
Try 1:
def fakeopen (filename, mode = 'r', buffering = 0):
... raise NotImplementedError ...
__builtins__.open = fakeopen __builtins__.file = fakeopen open ('/dev/urandom', 'rb')
Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in fakeopen NotImplementedError Try 2 (no, you don't need another interpreter instance!)
def fakeopen (filename, mode = None, buffering = None):
... raise NotImplementedError ...
newbuiltins = dict (vars (__builtins__)) env = {} newbuiltins['open'] = fakeopen newbuiltins['file'] = fakeopen env['__builtins__'] = newbuiltins # Try to do something VERBOTEN. script = "f = open('/dev/random','r'); randombyte = f.read (1)" exec script in env
Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<string>", line 1, in <module> File "<stdin>", line 2, in fakeopen NotImplementedError
# now see what nested exec does..
...
script = """exec "f = open('/dev/random','r'); randombyte = f.read(1)";"""
exec script in env
Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<string>", line 1, in <module> File "<string>", line 1, in <module> File "<stdin>", line 2, in fakeopen NotImplementedError
# still works!
... Unless there is some way to access open() via modules' __builtins__ attribute, or functions' func_globals attribute.. And there is. But it's caught! script = """exec "import os; f = os.__builtins__['open']('/dev/random','r'); randombyte = f.read(1);f.close()";"""
exec script in env
Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<string>", line 1, in <module> File "<string>", line 1, in <module> IOError: file() constructor not accessible in restricted mode
I am running Python 2.6 here, and evidently setting __builtins__ in a globals dictionary activates restricted mode for anything running in it. Testing nestedness
# now for extreme convolution
...
script = """exec "f = open('/dev/random','r'); randombyte =
f.read(1);f.close()" in os.__builtins__"""
exec script in env
Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<string>", line 1, in <module> File "<string>", line 1, in <module> IOError: file() constructor not accessible in restricted mode :D Testing silly degree of nestedness:
#and ultimate convolution
...
script = """exec "exec \\\"f = open('/dev/random','r'); randombyte =
f.read(1);f.close()\\\"" in os.__builtins__"""
exec script in env
Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<string>", line 1, in <module> File "<string>", line 1, in <module> File "<string>", line 1, in <module> IOError: file() constructor not accessible in restricted mode So that's pretty locked-down (I haven't tested the os module -- get a checkout of SVN python if you want to do that.) Greg, in Python 2.6: Python 2.6a0 (trunk:52884, Dec 1 2006, 14:21:57) [GCC 4.0.3 (Ubuntu 4.0.3-1ubuntu5)] on linux2 Type "help", "copyright", "credits" or "license" for more information.
(3).__class__.__bases__[0].__subclasses__()[-3]
<type 'deque_reverse_iterator'>
(3).__class__.__bases__[0].__subclasses__()[-29]
<type 'file'>
# It's a nice hack, but it doesn't help you evade restrictions: exec "(3).__class__.__bases__[0].__subclasses__()[-29]('/dev/urandom')"
in env Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<string>", line 1, in <module> IOError: file() constructor not accessible in restricted mode The two other major issues are looping-forever (which can't be fixed really, except by restricting execution time) and memory usage (I accidentally created a infinite loop today that expanded Python's memory usage to 500mb). Greg's idea of running as a seperate process is good for addressing those. Re: imports -- probably the only fully safe way is to prohibit them completely, and pre-import chosen safe modules for your scripts' use.