Semgrep提示importlib.import_module存不可信输入风险,求白名单实现方案
问题说明
你在使用importlib.import_module拼接动态配置值导入模块时,触发了Semgrep的安全告警,告警信息如下:
importlib.import_module()函数中传入不可信用户输入会允许攻击者加载任意代码,请避免在importlib.import_module()中使用动态值,或使用白名单防范不可信代码运行。
触发告警的原始代码:
channel_module = import_module("src.main.core_prj.prj_" + config['subscription'].lower())
解决方案
方案1:白名单校验(最推荐,可直接消除告警)
提前定义允许导入的订阅标识列表,导入前先校验传入值是否在合法范围内,不符合则直接抛出异常,从根源上避免加载任意模块:
from importlib import import_module # 手动维护所有允许的订阅标识,可根据业务实际情况调整 ALLOWED_SUBSCRIPTION_TYPES = {'wechat', 'alipay', 'douyin', 'xiaohongshu'} sub_type = config['subscription'].lower() # 先做白名单校验 if sub_type not in ALLOWED_SUBSCRIPTION_TYPES: raise ValueError(f"非法的订阅类型: {sub_type}") # 校验通过后再执行导入 channel_module = import_module(f"src.main.core_prj.prj_{sub_type}")
如果允许的订阅模块数量较多,可自动扫描对应目录生成白名单,无需手动维护:
import os from importlib import import_module PRJ_MODULE_DIR = "src/main/core_prj" # 自动扫描目录下所有prj_开头的模块,提取合法订阅标识 ALLOWED_SUBSCRIPTION_TYPES = set() for filename in os.listdir(PRJ_MODULE_DIR): if filename.startswith("prj_") and (filename.endswith(".py") or os.path.isdir(os.path.join(PRJ_MODULE_DIR, filename))): sub_type = filename.split("_", 1)[1].rsplit(".py", 1)[0] ALLOWED_SUBSCRIPTION_TYPES.add(sub_type) sub_type = config['subscription'].lower() if sub_type not in ALLOWED_SUBSCRIPTION_TYPES: raise ValueError(f"非法的订阅类型: {sub_type}") channel_module = import_module(f"src.main.core_prj.prj_{sub_type}")
方案2:静态映射表
直接建立订阅标识和模块路径的静态映射,完全避免动态字符串拼接的风险:
from importlib import import_module CHANNEL_MODULE_MAP = { "wechat": "src.main.core_prj.prj_wechat", "alipay": "src.main.core_prj.prj_alipay", "douyin": "src.main.core_prj.prj_douyin", "xiaohongshu": "src.main.core_prj.prj_xiaohongshu" } sub_type = config['subscription'].lower() module_path = CHANNEL_MODULE_MAP.get(sub_type) if not module_path: raise ValueError(f"非法的订阅类型: {sub_type}") channel_module = import_module(module_path)
方案3:输入字符过滤(仅作为补充缓解方案,优先选白名单)
如果业务场景不便维护白名单,可以限制输入仅包含小写字母、数字等安全字符,避免路径遍历或跨目录导入的风险:
import re from importlib import import_module sub_type = config['subscription'].lower() # 限制输入仅包含小写字母和数字,长度不超过20位,可根据业务调整规则 if not re.fullmatch(r'[a-z0-9]{1,20}', sub_type): raise ValueError(f"非法的订阅类型格式: {sub_type}") channel_module = import_module(f"src.main.core_prj.prj_{sub_type}")
内容的提问来源于stack exchange,提问作者Sarvendra Singh
相关产品推荐
相关产品推荐

