嵌入式Python 3.6中禁用内置模块导入的实现方案咨询
这是嵌入式Python场景里很常见的安全需求,我给你几个经过验证的靠谱方案,你可以根据自己的C++嵌入场景选择合适的组合:
1. 替换内置的__import__函数
Python里的import本质是调用内置的__import__函数,我们可以把它替换成自定义的限制版本,只允许导入你的C++模块:
import builtins # 先保存原始的__import__函数 original_import = builtins.__import__ def restricted_import(name, *args, **kwargs): # 这里列出你允许导入的自定义模块名称 allowed_modules = {"my_custom_cpp_module"} if name not in allowed_modules: raise ImportError(f"导入模块 '{name}' 是不被允许的") # 允许的模块调用原始导入函数 return original_import(name, *args, **kwargs) # 替换内置的__import__ builtins.__import__ = restricted_import
注意点:这个方法简单直接,但如果用户能拿到builtins的引用,理论上可以改回原始函数。所以最好在初始化Python解释器的第一时间就设置这个替换,同时结合其他限制手段。
2. 通过sys.modules占位阻止内置模块导入
我们可以提前把所有内置模块在sys.modules里设为None或者空对象,这样当用户尝试导入时会直接报错:
import sys # 获取所有Python内置模块的名称 builtin_module_list = sys.builtin_module_names # 把内置模块都占位成None,阻止导入 for module_name in builtin_module_list: sys.modules[module_name] = None # 保留你需要的自定义模块(如果已经加载) allowed_modules = {"my_custom_cpp_module"} for module_name in list(sys.modules.keys()): if module_name not in allowed_modules and module_name != "sys": del sys.modules[module_name]
注意点:不要删除sys、__main__这类核心模块,否则会导致Python解释器崩溃。另外,用户如果手动修改sys.modules可能绕开限制,所以需要配合解释器的沙箱防护。
3. 在C++嵌入层面设置导入钩子(最推荐)
因为你是用C嵌入Python的,直接在C层面修改导入逻辑会更安全,用户在Python脚本里很难绕开。这里是一个简单的实现示例:
#include <Python.h> #include <cstring> // 自定义的限制导入函数 static PyObject* restricted_import(PyObject* self, PyObject* args) { const char* module_name; if (!PyArg_ParseTuple(args, "s", &module_name)) { return NULL; } // 定义允许导入的自定义模块列表 const char* allowed_modules[] = {"my_custom_cpp_module", NULL}; int is_allowed = 0; for (int i = 0; allowed_modules[i] != NULL; ++i) { if (strcmp(module_name, allowed_modules[i]) == 0) { is_allowed = 1; break; } } if (!is_allowed) { PyErr_SetString(PyExc_ImportError, "Import is not permitted"); return NULL; } // 调用原始的__import__函数 PyObject* builtins = PyEval_GetBuiltins(); PyObject* original_import = PyDict_GetItemString(builtins, "__import__"); return PyObject_CallFunctionObjArgs(original_import, PyUnicode_FromString(module_name), NULL); } // 定义模块方法 static PyMethodDef RestrictedMethods[] = { {"__import__", restricted_import, METH_VARARGS, "Restricted import function"}, {NULL, NULL, 0, NULL} }; // 定义限制模块 static struct PyModuleDef RestrictedModule = { PyModuleDef_HEAD_INIT, "_restricted_import", NULL, -1, RestrictedMethods }; // 模块初始化函数 PyMODINIT_FUNC PyInit__restricted_import(void) { return PyModule_Create(&RestrictedModule); } // 在初始化Python解释器后调用这个函数设置限制 void setup_restricted_import() { PyObject* builtins = PyEval_GetBuiltins(); PyObject* restrict_module = PyImport_ImportModule("_restricted_import"); PyObject* new_import = PyObject_GetAttrString(restrict_module, "__import__"); PyDict_SetItemString(builtins, "__import__", new_import); // 释放引用,避免内存泄漏 Py_DECREF(restrict_module); Py_DECREF(new_import); }
优势:这种底层的钩子完全在C++层面控制,Python脚本无法直接修改,安全性最高,非常适合你的嵌入式场景。
4. 用AST语法树预检查过滤import语句
在执行用户脚本前,我们可以用ast模块解析脚本的语法树,直接拦截所有import和from ... import ...语句:
import ast class ImportBlocker(ast.NodeTransformer): def visit_Import(self, node): raise SyntaxError("禁止使用import语句") def visit_ImportFrom(self, node): raise SyntaxError("禁止使用from...import语句") def validate_script(script_content): try: tree = ast.parse(script_content) transformer = ImportBlocker() transformer.visit(tree) ast.fix_missing_locations(tree) return compile(tree, "<user_script>", "exec") except SyntaxError as e: raise e # 使用示例 user_script = """ import os print(os.getcwd()) """ try: compiled_script = validate_script(user_script) exec(compiled_script) except SyntaxError as e: print(f"脚本验证失败: {e}")
注意点:这个方法能提前拦截显式的import语句,但要注意用户可能用exec、eval动态生成import代码,所以需要同时限制这些内置函数的使用。
推荐组合方案
为了达到最好的防护效果,建议你:
- 优先使用C++层面的导入钩子(最安全)
- 配合AST语法预检查,提前拦截import语句
- 再加上sys.modules占位,作为最后一层防护
这样多层限制基本能完全阻止用户导入任何内置模块,只允许使用Python语言本身和你的自定义C++模块。
内容的提问来源于stack exchange,提问作者kovacsv

