[Python-Dev] Reviving restricted mode?

Ivan Krstić krstic at solarsail.hcs.harvard.edu
Mon Feb 23 03:09:19 CET 2009


On Feb 22, 2009, at 5:15 PM, Martin v. Löwis wrote:
> What is the objective of this code? Is it a complete sandbox?
> If not, is a complete sandbox based on it available somehow for
> review?

 From a cursory look at Tav's CPython patch, I'd describe it as follows:

     Requires: an existing Python code execution environment
               which does not expose unsafe interpreter
               functionality to the (untrusted) code it runs,

     Provides: a way for the trusted code outside of that
               restricted environment to wrap functions
               (which may reference trusted objects) in
               closures which can be passed into the restricted
               environment as completely opaque objects
               whose internal state (including any referenced
               trusted objects) cannot be accessed from the
               untrusted code.

When I say 'requires', I mean 'requires to be useful'; the patch can  
be applied to vanilla CPython, but isn't useful on its own.

Building blocks for a restricted execution environment as required by  
the patch are commonly provided in templating libraries; Tav mentions  
Genshi in particular. By default, Genshi templates don't come with  
security built in -- you can do what you please in a template.  
However, since Genshi manually constructs a Python AST from Python  
code in the template, one can restrict the AST and modify the builtins  
exposed to the template environment, making things like filesystem  
access, import and other sensitive system facilities unavailable.

Even with those unavailable, untrusted code can break out of  
containment by accessing object.__subclasses__ or gaining access to  
gi_frame and gi_code. This means you can't, for instance, pass into  
the untrusted code a closure which operates on trusted objects. Tav's  
patch addresses this particular problem.

To give you a complete sandbox to play with, I just violently ripped  
out the relevant code from Genshi, added some glue, and slapped it all  
together in a single file along with Tav's pure-Python code which is  
functionally equivalent to the CPython patch he submitted. The result,  
tested on 2.5.1 and nothing else:

     <http://radian.org/~krstic/sandbox.py>

As is, sandbox allows you to execute untrusted Python code which won't  
have access to import, __import__, eval, exec, execfile, open, and to  
which you can pass closures which reference trusted objects which the  
untrusted code can't get at.

Example:

 >>> import sandbox
 >>> import sys
 >>> import md5
 >>> def trusted_pwd_closure(password):
...    def get_pwd():
...        return md5.md5(password).hexdigest()
...    return get_pwd
...
 >>> context = dict(pwd=trusted_pwd_closure('secret'))
 >>> sandbox.run_string("print pwd()", context)
 >>> sandbox.run_string("print pwd.func_closure[0].cell_contents",  
context)
[snip]
AttributeError: 'function' object has no attribute 'func_closure'

Without Tav's patch, the untrusted code can extract our password  
('secret') from the closure with that last statement.

To run whole files in the sandbox:
 >>> sandbox.run_file('/some/path/file.py')

Finally, disclaimer: I put this together in *15 minutes*. I'm sure I  
missed something, this isn't secure, etc. It's just a proof of  
concept. Void where prohibited, etc.

Cheers,

--
Ivan Krstić <krstic at solarsail.hcs.harvard.edu> | http://radian.org



More information about the Python-Dev mailing list