第三方 AI 库的依赖风险:你的 AI 应用依赖了多少不可信的组件

我最早做AI项目时有个坏习惯:缺什么功能就 pip install,挑下载量最高的那个库,import 进去,剩下的交给运气。真正让我觉得不对劲的是有一次 pip freeze,我数了一下自己的依赖——203 个包。接着我搜了其中几个不太眼熟的:有的维护者已经消失一年,有的 GitHub 仓库半年前就没人提交了,却还在持续发布新版本。我忽然意识到一件事:我不是在写 AI 应用,我是在管理一支我没见过面的外包团队,人数超过两百,而他们的入职面试,我一次都没参加过。

AI technology illustration

这篇文章想聊一个很具体的问题:当你 import 一个第三方 AI 库的时候,你到底在信任谁?答案可能比你想象的要庞大得多。

你的 import 背后,站着一整个你从未见过的开发团队

装一个 transformers,你的依赖列表里会多出 torch、tokenizers、safetensors、huggingface-hub 这些直接依赖。但每个直接依赖又各自拖家带口:torch 带上 filelock、sympy、networkx、jinja2;huggingface-hub 又带来 requests、packaging、pyyaml、tqdm。数到最后,一个最普通的 LLM 应用的依赖树轻易超过 100 个包。如果你用 LangChain 这类框架,这个数字还会更高。

依赖类型 定义 你通常在哪里看到
直接依赖 你的代码里 import 的那些库 requirements.txt 里
传递依赖 直接依赖又依赖的库,层层嵌套 pip freeze 的全部输出
真实依赖 被你信任、但你从没读过源码的那几百人 没有任何文件会列出来

我最初以为第三方依赖风险就是"某个包有漏洞"。后来看了 Snyk 的开源安全报告 才发现,过去几年供应链攻击的增长是以"倍"为单位的,攻击者早就学会了一件事:与其费劲打穿你的服务器,不如往你依赖的某个小包里塞一行代码,然后等你自己把它运行起来。

攻击者的工作方式:等你亲手把后门装进项目

针对 AI 依赖的供应链攻击,基本就三招。

  • 拼写攻击(typosquatting):你随手想装 openai,敲成了 openai-py,或者把 transformers 打成 transfomers。攻击者事先注册好这些"高仿名",里面装着能偷环境变量和 API key 的代码。安全公司 Phylum、ReversingLabs 都曝光过 PyPI 上成批出现的这类恶意 AI 包。
  • 依赖混淆(dependency confusion):你的构建环境里有一个私有包仓库,某个内部包在私有仓库里找不到,包管理器就自动去公共 PyPI 拉。攻击者提前在公共 PyPI 上传一个同名包,你的 CI 环境就会乖乖下载并执行他的代码。2021 年安全研究员 Alex Birsan 用这个方法让苹果、微软、PayPal 等公司的内部系统把他上传的包拉进了构建流程——他在博客里说,整个过程只是"上传包、等回连"。
  • 维护者投毒:更省事的是直接搞到热门库的维护者账号,或者收购放弃维护的知名项目,释放一个"正常"的新版本,里面夹带挖矿程序或窃密脚本。npm 和 PyPI 都出过这类事件。

AI 库尤其容易中招,原因很现实:AI 开发者本来就在频繁安装新库——今天试一个 agent 框架,明天装一个 embedding 工具。这种"试用文化"本身就是供应链攻击最好的土壤。

你以为你在信任一个库,其实你在信任提名这个库的搜索结果、发布它的账号、以及它背后每一个现任和曾经的维护者。

模型文件不是数据,是可执行代码

这一节想讲一个让我自己都后怕的事实。很多 AI 应用不只是依赖代码包,还依赖模型文件。而模型文件,在一种情况下来源完全不可信:网上随便下载来的。

torch.load() 加载一个 .pt 文件,底层用的是 Python 的 pickle 反序列化。Pickle 有个特性:反序列化时,对象里可以嵌入"执行任意函数"的指令。也就是说,一个看起来只是"权重张量"的文件,在你加载的瞬间,可以运行攻击者写好的任意代码。更阴的是,模型本身还能正常加载、正常推理,因为恶意代码被伪装成一段无害数据,夹在张量中间。

"如果你用 torch.load 加载来自不可信来源的模型,该模型可以执行任意代码。"——PyTorch 官方序列化文档

Hugging Face 这类模型平台虽然做了扫描,但模型文件数量太大了,扫描很难覆盖全部。安全团队在平台上发现过真实存在的恶意模型,它们会被下架,但下架之前,已经有人下载过、加载过。

解决方案是有的:Safetensors,一个专门为"只存张量"设计的格式。它本身不支持执行代码,加载模型就不再等于运行程序。PyTorch 和 Hugging Face 生态现在都默认优先支持它。用一句话记住这个区别:

格式 加载时能不能执行代码 适合谁
PyTorch .pt / pickle 能,且不需要任何额外配置 只加载自己训练出的模型时
Safetensors 不能,纯数据 任何从网上下载模型的场景

在"零风险"不存在的前提下,把风险压到可控

看到这里你可能会问:那是不是什么都不能装了?不是。我的看法是,依赖风险无法清零,但可以从"不可见"变成"可见"。你能做的第一件事是搞清楚自己到底装了什么:

  1. 锁定版本:用 pip-tools 或 poetry 生成完整的 requirements.lock,把传递依赖的精确版本固定下来,并在 CI 里校验。不要用裸 pip install + requirements.txt,那等于在邀请明天版本漂移和后门更新。
  2. 定期扫描:pip-audit 或 osv-scanner 会用 OSV、PyPI 的安全数据库比对你的依赖列表,找到已知漏洞。把它挂进 CI,每次提交都跑一遍。
  3. 模型文件只用 safetensors:拒绝任何来自外部的 .pt、.pkl 文件。非用不可的话,放在隔离环境(容器/沙箱)里加载。
  4. 装包前先查户口:去 PyPI 页面和 GitHub 仓库看一眼:维护者还在不在?最近的更新是否正常?下载量和仓库状况是否匹配?这个习惯成本极低,能过滤掉大半的仿冒包。
  5. 对待第三方 AI 工具链:用企业内部私有仓库(如 devpi)做统一入口,设置只允许从审核过的源拉包。

这些工具的底层逻辑,是让依赖链变得可审计、可追溯。美国 CISA 一直在推进的 SBOM(软件物料清单),本质上就是同一件事:把"你到底依赖了什么"从人的脑洞里搬出来,变成一份结构化的清单。同时,监管也没闲着。NIST 在 2023 年发布了 AI 风险管理框架,欧盟《网络弹性法案》也即将生效。这些动作的指向其实一致:让 AI 系统的依赖从一开始就可见。

顺手解释几个术语
SBOM(Software Bill of Materials)
一份列出软件所有组成部分及其来源、版本、许可证的清单。CISA 和美国行政命令 14028 都要求关键软件供应商提供。
pip-audit
Python 生态的依赖漏洞扫描工具,会用 OSV 数据库检查当前环境或 requirements 文件中的包是否存在已知漏洞。
osv-scanner
Google 开源的漏洞扫描器,支持多种生态,直接读锁文件给出结果。

你不可能看清每个依赖,但可以让它变得可见

我现在依然会 pip install,但心态已经变了。每次按下回车之前,我会想一个问题:这个包值得信任吗?如果它的维护者明天消失,我的项目怎么办?如果它今天被攻击者接管,我的 API key 会去哪里?依赖安全不是一条可以一劳永逸的规则,而是一种需要持续做的状态管理。

AI 生态把这个问题放大了:包更多、更新更快、模型文件也加入了执行链。开源社区的信任机制却还停留在远古时代——包管理器默认信任注册中心,注册中心没有能力对每个包做代码审计。SBOM 和签名机制正在慢慢补上这个缺口,但短期内,对个体开发者来说,最优策略不是"彻底不依赖",而是"让依赖可见、锁定、可回滚"。

说到底,你 import 的不是一个库,是你对这个开源世界的信任。信任可以被管理,但它不能被忽略。

原创文章,作者:guanweilu,如若转载,请注明出处:https://guanweilu.cn/article/1821.html

赞 (0)
上一篇 2天前
下一篇 2天前

相关推荐