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

Posted

技术标签:

【中文标题】在嵌入式 Python 中禁用内置模块导入【英文标题】:Disable built-in module import in embedded Python 【发布时间】:2018-08-06 02:49:21 【问题描述】:

我在我的应用程序中嵌入了 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 ();

你知道有什么方法可以实现吗?

【问题讨论】:

一瞥:删除evalexecsysos 当然我想禁用除我自己的模块之外的所有内容。 请注意,您可以通过使用自定义函数覆盖builtins.__import__ 属性来控制所有import 语句。 (这不会使 Python 对恶意用户具有鲁棒性,但可以在您的简单示例中使用。) 【参考方案1】:

长期以来,Python 一直无法创建安全沙箱(请参阅 How can I sandbox Python in pure Python? 作为起点,如果您愿意,可以深入了解旧的 python-dev discussion)。以下是我认为是您最好的两个选择。

预扫码

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

在您的情况下,您要查找的将是任何import 语句(简单)和任何***变量(例如,在a.b.c 中,您关心a 和可能的a.b 对于给定的@ 987654333@) 未“批准”。这将使您能够在运行之前在任何不干净的代码上失败。

这里的挑战是,即使是简单的混淆代码也会绕过您的检查。例如,在给定其他模块或全局变量的情况下,这里有一些导入模块的方法,基本扫描 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()

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

运行时审计

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

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

repo 中的 Programs/spython.c 文件是从 C 进行审计的一个相当彻底的示例,而从 Python 进行审计看起来更像这样(取自 my talk 关于此 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)

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

【讨论】:

很详细的回答,谢谢,我觉得这是一个很好的起点。一个问题:为什么我必须检查***变量?在这种情况下,什么样的变量会造成麻烦? @kovacsv 我的评论开始变长,所以我改为添加到答案中。 非常感谢,现在我明白问题的复杂性了。

以上是关于在嵌入式 Python 中禁用内置模块导入的主要内容,如果未能解决你的问题,请参考以下文章

Python模块导入和常用内置方法

Python常用内置模块

python内置模块

Python基础

06Python 内置函数redisyagmailnnlog导入模块的实质

Python内置模块181101