Preventing Python code from importing certain modules?

Each Answer to this Q is separated by one/two green lines.

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?

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

Have you checked the 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.

8 years, yeesh, and nobody has figured this one out? :/

You can override the import statement or aka the __import__ function.

This is just a tested scribble-code because I couldn’t find any legit reference:

import importlib

def secure_importer(name, globals=None, locals=None, fromlist=(), level=0):

    if name != 'C': print(name, fromlist, level)

    # not exactly a good verification layer
    frommodule = globals['__name__'] if globals else None
    if name == 'B' and frommodule != 'C':
        raise ImportError("module '%s' is restricted."%name)

    return importlib.__import__(name, globals, locals, fromlist, level)

__builtins__.__dict__['__import__'] = secure_importer

import C

and here’s the tests for that code:

Python 3.4.3 |Anaconda 2.3.0 (32-bit)| (default, Mar  6 2015, 12:08:17) [MSC v.1600 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> ================================ RESTART ================================
B ('f',) 0
imported secure module
>>> from B import f
B ('f',) 0
linecache None 0
encodings.utf_8 ['*'] 0
Traceback (most recent call last):
  File "<pyshell#0>", line 1, in <module>
    from B import f
  File "\home\tcll\Projects\python\test\restricted imports\", line 11, in secure_importer
    raise ImportError("module '%s' is restricted."%name)
ImportError: module 'B' is restricted.
>>> import C

Please do not comment about me using Python34, I have my reasons, and it’s my primary interpreter on Linux specifically for testing things (like the above code) for my primary project.

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…?

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!

You can register a custom MetaPathFinder as the first element of sys.meta_path. This finder can maintain a whitelist of modules and return None if the import is acceptable, in order to delegate to other finders, or raise ImportError if the import is illegal.

from import MetaPathFinder
import sys

class Whitelist(MetaPathFinder):
    def __init__(self, whitelist):
        self.whitelist = whitelist

    def find_spec(self, fullname, path, target=None):
        if fullname not in self.whitelist:
            raise ImportError(fullname)

sys.meta_path.insert(0, Whitelist({'math'}))

import math  # works
import typing  # raises ImportError

However at interpreter startup already a bunch of modules are automatically imported. You can check this by using the -v flag, e.g. python -vc "" (it’s a long list, so I won’t copy it here).

So you also need to clear those modules from sys.modules: sys.modules.clear().

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.

The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 .