◄ 所有文章
AI Security

开源模型及其风险:如何检查模型文件

NW Nils Weiser •11 OCT 2026 •—

AI 模型是一个神经网络:一套规定如何计算的架构,加上数百万到数十亿个学习得到的数值,也就是权重。但装着它的文件可以包含更多东西:在加载时被执行的指令。不加检查就加载别人给的 .pt 文件,最坏的情况下等于启动了一段别人写的程序。

Hugging Face 上已有超过两百万个公开模型,而大多数团队下载它们时,就像安装一个 npm 包一样不设防。这篇文章是关于模型文件的深入篇:哪些格式能执行什么,扫描器会漏掉什么,以及如何在第三方模型进入生产之前检查它。从数据到智能体工具的整条供应链概览,见《AI 供应链的新风险》。

模型文件何时会变成程序

许多模型格式建立在 Python 的 pickle 之上。pickle 保存的不只是数值,还有一串“如何重建对象”的指令。通过 __reduce__ 方法,对象可以指定任意函数和任意参数,加载器会照单执行。Python 官方文档说得很直白:这个模块并不安全,只应反序列化你信任的数据。

import os, pickle

class Model:
    def __reduce__(self):
        # 任意命令
        return (os.system, ("id",))

# 看起来像个模型
pickle.dump(Model(), open("model.pkl", "wb"))
# 实际执行了命令
pickle.load(open("model.pkl", "rb"))

所以,加载时会发生什么,取决于格式:

格式 加载时会发生什么 剩余风险
.pkl、.pt、.bin
Pickle
文件一被加载,就可能执行任意代码。 高 只加载你愿意当作自己代码对待的文件。
.safetensors JSON 头部加原始张量数据,没有可执行内容。Trail of Bits 在 2023 年的审计没有发现会导致代码执行的严重漏洞。 低 权重本身仍可能带有后门。
.h5、.keras
Keras
Lambda 层// Lambda 层Keras 模型中的一种构件,可以把任意 Python 代码作为计算步骤嵌入模型。它以字节码形式保存,在加载模型时就可能被执行。例子一个名字看似无害的层 scale_inputs,暗中向外建立连接。把 Python 代码带进了模型架构(CVE-2024-3660)。 中 检查各层,保持 Keras 为最新版本。
.gguf// GGUF用于在本地运行语言模型的文件格式,例如配合 llama.cpp 使用。 不使用 pickle,但文件里带有聊天模板:在 llama-cpp-python 中,这曾导致代码执行(CVE-2024-34359);2025 年 Pillar Security 还展示了在推理时注入指令的模板。 中 阅读模板,给运行时打补丁。

有两点很容易被忽略。第一:weights_only=True 从 PyTorch 2.6 起成为默认值,它会限制反序列化器的能力。但这不是保证:在 2.5.1 及更早版本中,CVE-2025-32434 即使设置了该参数仍可执行代码。第二:Safetensors 防的是加载时的代码,防不了权重里“学到”的东西。

架构中的代码:Keras Lambda 层

pickle 并不是把代码带进模型的唯一途径。Keras 提供 Lambda 层:它可以把任意 Python 表达式作为一个计算步骤挂进模型。按照 Keras 文档 的说法,这类层以 Python 字节码的形式保存,文档自己就称之为“potentially unsafe”。

CERT/CC 对后果的描述很直接:加载第三方的 Keras 模型,可能就是在运行第三方的代码。因此 Keras 3 默认拒绝加载函数以字节码形式保存的 Lambda 层(safe_mode)。但还有两个缺口。在 Keras 2 以及 3.12 之前的 Keras 3 中,这项检查对经典的 .h5 文件并不生效,CERT/CC 的说明也是这样写的。直到 2025 年 10 月发布的 3.12,Keras 才把 safe_mode 传递给 H5 加载器。而这层保护本身也在 2025 年被绕过:CVE-2025-1550 在开启 safe_mode 的情况下仍可执行代码。

换格式在这里帮不上忙:Safetensors 只保存权重,架构仍然留在 Keras 文件或代码里。有用的做法是在加载模型之前先看一眼层列表。ModelScan 会在 .h5 文件中专门查找 Lambda 层(安装时带上附加组件 modelscan[tensorflow,h5py])。只要模型不是你自己构建的,其中出现的任何 Lambda 层或自定义层都值得追问。

模型文件已经出过哪些事

2024 年 2 月,JFrog 在 Hugging Face 上发现了大约 100 个模型会在加载时执行代码,其中一个会建立反向 shell// Reverse Shell被入侵的机器主动连接攻击者,并把命令行交给对方。。JFrog 自己也指出,这些载荷// Payload(载荷)攻击中真正造成破坏的那部分。这里指被植入的代码。中大多数可能是研究人员或漏洞赏金猎人的测试。但原理同样成立。

2025 年以来的三个变化让情况更加棘手。

1 // 扫描器可以被骗过

Hugging Face 会扫描上传的文件,但它自己也说这并非百分之百可靠。2025 年 2 月,ReversingLabs 披露了两个模型(nullifAI):它们用 7z 而不是 ZIP 压缩,pickle 流在载荷之后立即中断。它们没有被标记为不安全,载荷却照样执行。同一年,Sonatype 报告了四种、JFrog 又报告了三种绕过 picklescan 扫描器的方法。平台上的绿色对勾只是一个提示,不是结论。

2 // 名字不能证明来源

PoisonGPT 在 2023 年演示了这个原理:一个被刻意篡改的模型,上传在 EleuterAI 名下,而不是 EleutherAI。2025 年,Unit 42 描述了模型命名空间重用(Model Namespace Reuse):作者账号被删除或转移后,别人可以重新注册原来的名字,所有只按名字加载模型的流水线都会拉到陌生人的模型。据 Unit 42 称,Google 的 Vertex AI 和微软的 Azure AI Foundry 也受到影响。2026 年 5 月,一个仿冒 OpenAI 发布内容的仓库在被封禁之前登上了 Hugging Face 趋势榜第一名。据 HiddenLayer 判断,它的下载量几乎可以肯定是人为刷出来的。

3 // 适配器与转换器

模型很少单独出现。LoRA 适配器// LoRA 适配器一种小型附加文件,无需重新训练就能让基础模型适配某项任务。例子把通用语言模型调整为客服回复风格的适配器。和模型一样被四处分享,而研究人员证明,后门只需在一个适配器里训练一次,之后无需再训练就能与其他适配器合并。因此 OWASP LLM 应用十大风险把存在漏洞的 LoRA 适配器单独列为一项供应链风险。2024 年,HiddenLayer 展示了 Hugging Face 的 Safetensors 转换服务可以被劫持,从而以官方机器人的名义提交 Pull Request。凡是经过别人流水线的东西,都应视为一个新的制品。

风险、措施、工具

用一张表概括。这里提到的工具全部开源。

风险 措施 工具
模型文件中的代码 只允许 Safetensors 一种格式。绝不未经检查就加载 pickle。使用 PyTorch 2.6 及以上版本。 safetensors、Fickling、ModelAudit、ModelScan、picklescan
架构中的逻辑 加载前先列出所有层。保持 Keras 的 safe_mode 开启,并及时更新 Keras。 ModelScan、h5py
文件被替换 固定哈希和修订版本,校验签名。 sha256sum、带完整提交哈希的 revision=、OpenSSF Model Signing
伪造或被接管的仓库 为组织建立白名单。生产环境只从自己的注册表加载,绝不按名字直接从 Hub 拉取。 内部模型注册表、提交哈希固定
加载时触发载荷 首次加载只在没有网络的沙箱中进行,并采集遥测。 禁止出站流量的容器、sys.addaudithook
权重中的后门、被投毒的数据 用你知道正确答案的用例做自己的评测。上线前做红队测试。 garak、promptfoo
适配器与转换过的模型 把它们当作新模型对待:同样的关卡,单独扫描,单独批准。 与上面相同的扫描器

软件包、供应商和智能体工具各有各的风险,这些内容在概览篇里:《AI 供应链的新风险》。

工具清单里不会写的三个局限

没有哪个扫描器是完备的。Fickling 自己就提醒,文件即使通过检查也可能不安全;而 ModelScan 最近一次发布是在 2026 年 2 月。请准备两个扫描器,而不是一个。按照 Python 文档,审计钩子不是沙箱,恶意代码甚至可以把它关掉:它提供遥测,隔离要靠容器。另外,pip-audit 找的是已知漏洞,不是恶意软件包,它的文档明确说明了这一点。

五道关卡的准入流水线

如果模型能绕过工具直接进入生产环境,单个工具的作用就很有限。对策是一套固定的准入流程:五道关卡,无论来源声誉多好,每个制品都必须逐一通过。

GATE 1 隔离 GATE 2 来源 GATE 3 完整性 GATE 4 扫描 GATE 5 批准 无网络访问 组织、历史、模型卡 哈希 + 签名 文件、层、依赖 内部注册表 任何一道关卡失败 = 拒绝

从下载到批准的五道关卡。生产环境只加载通过第 5 道关卡的制品。

自己动手:三分钟完成一次扫描

你需要一个终端(macOS 或 Linux,Windows 下用 WSL)和 Python 3.10 到 3.12。三个扫描器都通过 pip 安装,最好装进一个全新的虚拟环境:

python3 -m venv .venv
source .venv/bin/activate
pip install fickling modelscan modelaudit

然后生成一个无害的测试文件。它用的是和上文相同的手法,只不过命令仅仅输出一个单词:

python3 - <<'EOF'
import os, pickle

class Model:
    def __reduce__(self):
        return (os.system, ("echo pwned",))

pickle.dump(Model(), open("test.pkl", "wb"))
EOF

现在在不加载文件的前提下检查它:

# 标准库自带:显示 pickle 中的指令
python3 -m pickletools test.pkl

# 把 pickle 还原成 Python 代码并给出评估
fickling --check-safety -p test.pkl

# 扫描并给出严重级别
modelscan -p test.pkl
modelaudit test.pkl

三个扫描器都会报警。Fickling 把这个文件称为“overtly malicious”,ModelScan 的输出是:

Unsafe operator found:
  - Severity: CRITICAL
  - Description: Use of unsafe operator 'system' from module 'posix'

posix 是 macOS 和 Linux 上 os.system 背后的模块。关键在于你没有做的那件事:你从头到尾都没有用 pickle.load() 打开这个文件。

用在真实项目上

用的还是同一套工具。加载时固定版本,这里用的是 transformers 库。org/model 代表 Hugging Face 上的仓库名,提交哈希可以在该仓库的历史记录里找到:

from transformers import AutoModel

model = AutoModel.from_pretrained(
    "org/model",
    # 完整提交哈希,而不是 "main"
    revision="877b84a8f93f2d619faa2a6e514a32beef88ab0a",
    use_safetensors=True,     # 不用 pickle
    trust_remote_code=False,  # 默认值,保持不变
)

这里的 commit hash 不只是装饰。如果仓库里没有 safetensors 文件,而 revision 又没有固定,transformers 会尝试通过 Hugging Face 转换机器人的 Pull Request 分支做自动转换,也就是“Silent Sabotage”中的那个服务。固定了 commit hash 就不会发生这种情况。而不设置 use_safetensors=True 时,库会悄悄退回到 pickle 文件。

第 3、4 道关卡的检查在存放 requirements.txt 的项目目录下运行:

# 第 3 道关卡:把哈希与公布的值比对
sha256sum model.safetensors

# 第 4 道关卡:从 Hub 获取仓库并做静态扫描
modelaudit hf://org/model

# 第 4 道关卡:检查依赖中的已知漏洞
pip install pip-audit
pip-audit -r requirements.txt

# 第 4 道关卡:生成物料清单并检查
syft . -o cyclonedx-json=sbom.json
grype sbom:./sbom.json

三点说明。Syft 和 Grype 是独立的程序,不是 Python 包,在 Mac 上用 brew install syft grype 安装。在较旧的 macOS 版本上,sha256sum 要换成 shasum -a 256。另外,只有当 requirements.txt 的每一行都带有哈希时,pip-audit --require-hashes -r requirements.txt 才能以严格模式运行;没有哈希,这条命令会直接中止。

只有发布者提供了签名,才谈得上校验签名。这时你需要签名文件、发布者的预期身份,以及其登录服务提供方:

pip install model-signing
model_signing verify ./model --signature model.sig \
  --identity "releases@example.org" \
  --identity_provider "https://accounts.example.org"

适配器和转换过的模型走同一条路。API 模型、MCP 服务器和提示词模板有哪些不同,见概览篇:《AI 供应链的新风险》。

工具解决不了的事

无论流水线做得多好,有三件事始终存在。

如果现在什么都没有,我会从这里开始:


来源:

JFrog: Malicious HF models ReversingLabs: nullifAI Sonatype: picklescan bypasses Unit 42: Namespace Reuse HiddenLayer: Silent Sabotage LoRATK Pillar: GGUF templates CERT/CC: Keras Lambda layers Safetensors audit PyTorch: CVE-2025-32434 OWASP LLM03:2025 Supply Chain OpenSSF Model Signing

想知道你们的模型和智能体供应链里到底有什么?我们聊聊。用 30 分钟,一起把这五道关卡对照你们的技术栈走一遍,没有任何义务。

NW
Nils WeiserAI 智能体专家 · 博登湖地区
与我合作 ▸

更多现场笔记

所有文章 ▸