I'm not familiar with Genshi -- what is the purpose of the AST
transformation here?
Traditionally, sandboxing environments in Python usually just exec the
untrusted code in an environment with a __builtins__ dict that limits
the built-in functions and overrides __import__ so as to provide an
import implementation that allows import of pure-Python modules but
not extensions (or only allows certain extensions that have been
previously audited, or provides wrappers or subsets of selected
others).
--Guido
On Sun, Feb 22, 2009 at 6:09 PM, Ivan Krstić
<***@solarsail.hcs.harvard.edu> wrote:
> 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ć <***@solarsail.hcs.harvard.edu> | http://radian.org
>
>
--
--Guido van Rossum (home page: http://www.python.org/~guido/)