在嵌入式 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 ();
你知道有什么方法可以实现吗?
【问题讨论】:
一瞥:删除eval
、exec
、sys
和os
。
当然我想禁用除我自己的模块之外的所有内容。
请注意,您可以通过使用自定义函数覆盖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 中禁用内置模块导入的主要内容,如果未能解决你的问题,请参考以下文章