必须用yaml.safe_load()替代yaml.load(),因后者默认调用fullloader,可执行!!python/object等恶意标签导致任意代码执行;safe_load()仅支持基础类型,从根本上杜绝反序列化漏洞。

为什么不能直接用 yaml.load() 读取不受信的 YAML 数据
因为 yaml.load() 默认使用 Loader=FullLoader(旧版是 Loader=UnsafeLoader),会执行任意 Python 对象构造,包括 !!python/object:os.system 这类危险标签。只要传入恶意字符串,就可能触发命令执行、文件读取等漏洞。
常见错误现象:YAMLError 没捕获到,但程序已静默执行了 shell 命令;或捕获了异常却仍用了不安全的加载方式。
- 永远不要对用户输入、网络响应、配置文件(除非 100% 确认来源可信)调用
yaml.load() - PyYAML ≥ 5.1 已默认禁用
load()的 unsafe 行为,但显式指定Loader仍可能绕过保护 - 即使加了
try/except YAMLError,也不能掩盖加载器本身不安全的事实
应该用 yaml.safe_load() 替代 yaml.load()
safe_load() 只支持 YAML 标准核心类型(str, int, list, dict, bool, null),拒绝所有自定义标签和对象构造,从根本上杜绝反序列化漏洞。
使用场景:解析配置文件、CI/CD 参数、API 返回的 YAML 内容、任何可能含不可信数据的输入。
- 替换写法:
yaml.load(data, Loader=yaml.Loader)→ 改成yaml.safe_load(data) -
safe_load()也会抛出YAMLError(比如格式错误),所以仍需try/except处理语法问题 - 它不支持
!!python/tuple或!include这类扩展语法——这正是你想要的“限制”
import yaml
<p>try:
config = yaml.safe_load(user_input)
except yaml.YAMLError as e:
print(f"YAML 解析失败:{e}")
config = None</p>
需要自定义类型时,必须显式注册 SafeLoader 的子类
如果业务确实要支持 !secret 或 !env 这类安全标签(即非 Python 执行,只是简单替换),不能退回到 FullLoader,而应继承 yaml.SafeLoader 并只添加可控的构造逻辑。
参数差异:SafeLoader 允许重载 construct_mapping / construct_scalar,但禁止重载 construct_python_* 相关方法。
- 错误做法:用
yaml.load(data, Loader=yaml.FullLoader)或yaml.CLoader(C 版本也不安全) - 正确做法:子类化
yaml.SafeLoader,用add_constructor()注册白名单标签 - 每个自定义构造器必须做输入校验,不能拼接或执行外部数据
class ConfigLoader(yaml.SafeLoader):
pass
<p>def construct_env(loader, node):
value = loader.construct<em>scalar(node)
return os.getenv(value, f"MISSING</em>{value}")</p><p>ConfigLoader.add_constructor('!env', construct_env)</p><h1>使用</h1><p>yaml.load(data, Loader=ConfigLoader) # ✅ 安全且可控</p>
PyYAML 升级与安装注意事项
旧版 PyYAML(load() 默认就是不安全的,且没有强制警告。升级后虽默认报错,但部分项目仍硬编码了 Loader=yaml.Loader,等于手动开后门。
性能 / 兼容性影响:safe_load() 比 load() 略慢(因多一层标签检查),但差距可忽略;不兼容的是那些依赖 !!python/object 的遗留配置——这类配置本就不该存在于生产环境。
- 检查当前版本:
pip show pyyaml,确保 ≥ 6.0(最新稳定版) - 禁用旧接口:在代码顶部加
yaml.load = None或用delattr(yaml, "load")防误用 - Docker 或 CI 环境中,避免用
pyyaml==3.13这类已知高危版本
真正容易被忽略的不是怎么 catch 异常,而是以为加了 try 就算安全——YAMLError 是语法错误,不是安全边界。安全边界只由加载器决定。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











