在嵌入式 Python 中禁用内置模块导入

kov*_*csv 5 c++ python python-embedding python-3.x

我在我的应用程序中嵌入了 Python 3.6,我想在脚本中禁用导入命令以防止用户导入任何 Python 内置库。我只想使用语言本身和我自己的 C++ 定义的模块。

Py_SetProgramName (L"Example");
Py_Initialize ();
PyObject* mainModule = PyImport_AddModule ("__main__");
PyObject* globals = PyModule_GetDict (mainModule);

// This should work
std::string script1 = "print ('example')";
PyRun_String (script1.c_str (), Py_file_input, globals, nullptr);

// This should not work
std::string script2 = "import random\n"
                      "print (random.randint (1, 10))\n";
PyRun_String (script2.c_str (), Py_file_input, globals, nullptr);

Py_Finalize ();
Run Code Online (Sandbox Code Playgroud)

你知道有什么方法可以实现这一目标吗?

Zoo*_*oba 4

Python 长期以来一直无法创建安全的沙箱(请参阅如何在纯 Python 中对 Python 进行沙箱?作为起点,然后如果您愿意,可以深入了解旧的python-dev 讨论)。以下是我认为是您最好的两个选择。

预扫码

在执行任何操作之前,请扫描代码。您可以在 Python 中使用AST 模块执行此操作,然后遍历树,或者可以通过更简单的文本搜索获得足够的结果。这可能适用于您的场景,因为您的用例有限 - 它不能推广到真正的任意代码。

您在您的案例中寻找的将是任何未“批准”import的陈述(简单)和任何顶级变量(例如,a.b.c您关心的a并且可能a.b针对给定的)。a这将使您能够在运行任何不干净的代码之前失败。

这里的挑战是,即使是极其模糊的代码也会绕过您的检查。例如,以下是一些在基本扫描找不到的其他模块或全局变量的情况下导入模块的方法import。您可能希望限制对__builtins__globals、某些/大多数/所有名称以及__double_underscores__某些类型的成员的直接访问。在 AST 中,这些将不可避免地显示为顶级变量读取或属性访问。

getattr(__builtins__, '__imp'+'ort__')('other_module')

globals()['__imp'+'ort__']('other_module')

module.__loader__.__class__(
    "other_module",
    module.__loader__.path + '/../other_module.py'
).load_module()
Run Code Online (Sandbox Code Playgroud)

(我希望这有点不言而喻,这是一个不可能的挑战,也是为什么这种沙箱方法从未完全成功。但它可能足够好,具体取决于您的具体威胁模型。)

运行时审计

如果您能够编译自己的 Python 运行时,您可以考虑使用(当前草案)PEP 551挂钩。(免责声明:我是本 PEP 的作者。)有针对最新3.73.6版本的实施草案。

本质上,这将让您为 Python 中的一系列事件添加挂钩并确定如何响应。例如,您可以侦听所有import事件并根据正在导入的模块确定在运行时允许还是失败,或者侦听compile事件以管理所有运行时编译。您可以通过 Python 代码(使用sys.addaudithook)或 C 代码(使用PySys_AddAuditHook)来执行此操作。

存储库中的Programs /spython.c文件是从 C 进行审计的相当彻底的示例,而从 Python 进行审计看起来更像是这样(摘自关于此 PEP 的讨论):

import sys

def prevent_bitly(event, args):
    if event == 'urllib.Request' and '://bit.ly/' in args[0]:
        print(f'WARNING: urlopen({args[0]}) blocked')
        raise RuntimeError('access to bit.ly is not allowed')

sys.addaudithook(prevent_bitly)
Run Code Online (Sandbox Code Playgroud)

这种方法的缺点是您需要构建和分发您自己的 Python 版本,而不是依赖系统安装。但是,一般来说,如果您的应用程序依赖于嵌入,那么这是一个好主意,因为这意味着您不必强制用户使用特定的系统配置。