Python如何阻止用户通过属性调用访问模块内部导入的其他模块
实现方案
一、隐藏模块内部导入的os
先解决禁止用户直接访问my_module.os的问题,可通过以下三种方式组合实现:
- 导入时使用私有别名:把
import os改为import os as _os,Python社区默认单下划线开头的成员为模块私有,不会被from my_module import *默认导入,普通用户也不会主动访问这类私有成员。 - 自定义模块级
__getattr__拦截访问:在你的模块文件末尾添加以下代码,主动拦截对os属性的访问:
def __getattr__(name): if name == "os": raise AttributeError("当前模块不允许直接访问os依赖") raise AttributeError(f"模块没有名为{name}的属性")
- 显式声明
__all__:只把你允许对外暴露的成员写入__all__列表,进一步避免未授权内容被导入。
二、用AST配合命名空间限制exec的可用模块
这个需求可以实现,核心方案是AST语法检测+自定义exec命名空间组合使用,具体步骤如下:
- 先通过AST遍历检测用户代码的违规操作
把用户传入exec的代码解析为AST树后,遍历所有节点拦截以下违规场景:
- 检测
Import、ImportFrom节点:检查导入的模块名是否在你预设的白名单内,不在则直接拒绝执行。 - 检测
Attribute节点:拦截所有访问你模块私有成员的操作,比如my_module.os这类属性访问。 - 检测
Call节点:禁止调用__import__、getattr、importlib.import_module这类容易绕过限制的反射函数,或者对这类调用的参数做严格的字面量校验。
注意:仅靠AST无法覆盖字符串拼接构造的动态导入场景,必须配合命名空间限制使用
- 给exec传入限制级命名空间(比AST检测更高效的兜底方案)
执行exec的时候手动指定globals和locals参数,只把你允许用户使用的模块、函数放到命名空间里,示例代码如下:
# 只保留允许用户使用的对象 allowed_globals = { "math": math, "my_module": my_module, # 仅保留允许的内置函数,删除__import__、getattr等危险接口 "__builtins__": {k: v for k, v in __builtins__.__dict__.items() if k in ["print", "int", "str", "sum"]} } exec(user_code, allowed_globals, {})
这种场景下就算用户写出import os的代码,也会因为命名空间里没有__import__函数执行失败,就算成功导入也没法把os放到可用的命名空间里。
三、注意事项
不存在100%安全的限制方案:Python的动态性决定了总有极端方法可以绕过限制,比如用字符串拼接构造反射参数、利用异常栈信息获取私有成员等,如果你需要执行完全不可信的用户代码,建议使用专用沙箱环境而不是单纯靠AST+exec限制。
普通业务场景下,私有别名+自定义__getattr__+自定义exec命名空间的组合已经足够覆盖绝大多数需求,可以挡住所有非刻意的违规调用。
内容的提问来源于stack exchange,提问作者Gleb
相关产品推荐
相关产品推荐

