views:

277

answers:

5

I'm writing an application where users can enter a python script and execute it in a sandbox. I need a way to prevent the exec'ed code from importing certain modules, so malicious code won't be as much of a problem. Is there a way to do this in Python?

A: 

You can overload the import mechanism. We used this to have a licensing system for plugins, you can easily have a whitelist / blacklist of module names.

Martin Beckett
A: 

Unfortunately, I think that what you're trying to do is fundamentally impossible. If users can execute arbitrary code in your application then they can do whatever they want. Even if you were able to prevent them from importing certain modules there would be nothing stopping them from writing equivalent functionality themselves (from scratch or using some of the modules that are available).

I don't really know the specifics of implementing a sandbox in Python, but I would imagine it's something that needs to be done at the interpreter level and is far from easy!

Evgeny
+6  A: 

Have you checked the python.org article on SandboxedPython, and the linked article?

Both of those pages have links to other resources.

Specifically, PyPi's RestrictedPython lets you define exactly what is available, and has a few 'safe' defaults to choose from.

nilamo
I should have mentioned, my app runs on Google App Engine. So, on one hand I have a lot of sandboxing already in place, but OTOH I'm not sure I can use RestrictedPython. I will certainly give it a try, thanks!
+2  A: 

Google App Engine's open source SDK has a detailed and solid implementation of mechanics to stop the importing of unwanted modules (to help detect code trying to import modules that aren't made available in the production instances of App Engine), though even that could be subverted if the user code was evil rather than just mistaken (production instances obviously have more layers of defense, such as simply not having those modules around at all;-).

So it all depends on how in-depth your defense needs to be. At one extreme you just stash the builtin __import__ somewhere else and replace it with your function that does all the checks you want before delegating to the __builtin__; that's maybe 20 lines of code, 30 minutes to implement and test thoroughly... but it might not protect you for long if somebody credibly offered me a million bucks to break into your system (and, hypothetically, I wasn't the goody-two-shoes kind of guy I actually AM, of course;-). At the other extreme you deploy an in-depth series of layers of defense that might take thousands of lines and weeks of implementation and testing work -- given that kind of resource budget I could surely implement something I would be unable to penetrate (but there's always the risk that somebody ELSE is smarter and more Python-savvy than I am, of course!).

So, how deep do you want to go, or rather, how deep can you AFFORD to go...?

Alex Martelli
I'm far from NSA level requirements/budget, so I suppose I'll opt for overriding __import__ and if hires the author of The Python Cookbook a million dollars to break the protection, will just bear the consequences..Any pointers to how to go about doing that? Being relatively new to Python I can't seem to find a simple solution.Thanks!
Production instances of App Engine are solidly protected, as I said with many layers of defense: I wouldn't know how to break into THOSE (if I did, I'd of course communicate it privately to my employer, or my many friends who work in the App Engine team, so the leak would be fixed immediately after I figured it out;-). Just do consider that just like your code can override builtin `__import__`, so may the code you're importing, so, defend against THAT, too; AND, use a "default deny" stance, where only specific modules are allowed, rather than specific ones being forbidden!-)
Alex Martelli
A: 

If you put None in sys.modules for a module name, in won't be importable...

>>> import sys
>>> import os
>>> del os
>>> sys.modules['os']=None
>>> import os
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named os
>>>
del-boy