26

我正在编写一个应用程序,用户可以在其中输入 python 脚本并在沙箱中执行它。我需要一种方法来阻止执行的代码导入某些模块,因此恶意代码不会成为太大的问题。有没有办法在 Python 中做到这一点?

4

7 回答 7

33

如果您将 None 放在 sys.modules 中作为模块名称,则 in 将无法导入...

>>> 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
>>>
于 2009-08-29T05:35:31.527 回答
19

您是否查看过有关 SandboxedPython 的 python.org文章链接文章

这两个页面都有指向其他资源的链接。

具体来说,PyPi 的RestrictedPython允许您准确定义可用的内容,并有一些“安全”默认值可供选择。

于 2009-08-29T04:44:34.967 回答
12

8 年了,是的,没有人想出这个?:/

您可以覆盖import语句或__import__函数。

这只是一个经过测试的涂鸦代码,因为我找不到任何合法的参考:

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

这是该代码的测试:

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\main.py", line 11, in secure_importer
    raise ImportError("module '%s' is restricted."%name)
ImportError: module 'B' is restricted.
>>> import C
>>> 

请不要评论我使用 Python34,我有我的理由,它是我在 Linux 上的主要解释器,专门用于为我的主要项目测试东西(如上面的代码)。

于 2017-12-17T11:03:27.577 回答
8

Google App Engine 的开源SDK具有详细而可靠的机制来阻止导入不需要的模块(以帮助检测尝试导入在 App Engine 的生产实例中不可用的模块的代码),尽管这可能是如果用户代码是邪恶的而不仅仅是错误的,则被颠覆(生产实例显然有更多的防御层,例如根本没有这些模块;-)。

所以这一切都取决于你的防守需要有多深入。在一种极端情况下,您只需将内置函数存储在__import__其他地方,然后用您的函数替换它,该函数在委托给__builtin__;之前执行您想要的所有检查。这可能是 20 行代码,30 分钟来实现和彻底测试......但如果有人可信地给我一百万美元来闯入你的系统,它可能不会保护你很长时间(而且,假设,我不是好人-当然,我实际上是那种穿双鞋的人;-)。在另一个极端,您部署了一系列深入的防御层,这可能需要数千行和数周的实施和测试工作——考虑到这种资源预算,我肯定可以实施一些将无法渗透(但总是有其他人比我更聪明,更精通 Python,当然!)。

那么,你想去多深,或者更确切地说,你能负担得起多深......?

于 2009-08-29T04:59:50.133 回答
1

不幸的是,我认为你想要做的事情基本上是不可能的。如果用户可以在您的应用程序中执行任意代码,那么他们可以为所欲为。即使您能够阻止他们导入某些模块,也没有什么可以阻止他们自己编写等效功能(从头开始或使用一些可用的模块)。

我真的不知道在 Python 中实现沙箱的细节,但我想这是需要在解释器级别完成的事情,而且远非易事!

于 2009-08-29T04:43:49.930 回答
0

您可以将自定义注册MetaPathFinder为 的第一个元素sys.meta_path。这个查找器可以维护一个模块的白名单,None如果导入是可接受的,则返回,以便委托给其他查找器,或者ImportError在导入非法时引发。

from importlib.abc import MetaPathFinder
import sys


class Whitelist(MetaPathFinder):
    def __init__(self, whitelist):
        super().__init__()
        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

然而,在解释器启动时,已经自动导入了一堆模块。您可以通过使用-v标志来检查这一点,例如python -vc ""(这是一个很长的列表,所以我不会在这里复制它)。

因此,您还需要从sys.modules:中清除这些模块sys.modules.clear()

于 2020-08-08T22:47:23.017 回答
-2

您可以重载导入机制。我们用它来建立插件许可系统,您可以轻松拥有模块名称的白名单/黑名单。

于 2009-08-29T04:43:36.407 回答