这次我们来看一个容易被低估的安全问题:HF 攻击。在很多 AI 项目里,大家习惯从 Hugging Face 拉模型、拉数据集,然后本地加载推理或微调。模型文件看起来只是一堆权重,实际却可能携带恶意代码、反序列化载荷,甚至在训练和部署流程里形成供应链攻击。这里的“HF 攻击”,指的就是针对 Hugging Face 生态的攻击手法,包括恶意模型、数据集投毒、依赖混淆、平台镜像伪造,以及围绕huggingface-cli到hf工具链迁移期间产生的新风险。
这个问题的严重性不是“某个账号被盗”或“某次推理跑出错误结果”那么简单。它影响的是整个 MLOps 流程:开发机、CI/CD、训练集群、推理服务、内部模型仓库,都会因为“一行代码下载模型”而暴露风险。更麻烦的是,很多人只在本地跑过一次torch.load(),根本不会意识到模型权重也能当攻击入口。本文会从攻击面、常见手法、检测思路、防护方案和团队落地这几个角度展开,并给出一套可执行的加固清单。
1. 核心威胁面速览
| 能力项 | 说明 |
|---|---|
| 攻击对象 | Hugging Face 平台、镜像站、本地模型仓库、MLOps 流水线 |
| 主要入口 | 恶意模型权重、数据集元数据、依赖包、README 链接、模型卡、token 泄露 |
| 技术关键点 | pickle 反序列化、torch.load()、trust_remote_code、Shell 命令注入、供应链投毒 |
| 常见危害 | 数据泄露、挖矿、横向移动、训练投毒、CI/CD 劫持、供应链污染 |
| 受影响角色 | AI 工程师、算法研究员、运维、安全团队、使用模型 API 的企业 |
| 推荐应对 | safetensors 优先、禁止盲加载、进程隔离、镜像校验、最小权限、离线私有仓库 |
| 批量任务风险 | 批量推理脚本一旦加载恶意模型,会扩散到整个任务队列 |
| API 接入风险 | 通过平台 API 调用不代表完全安全,仍需校验来源与响应 |
2. 为什么说“比预想严重得多”
2.1 信任模型太脆弱
Hugging Face 的核心理念是“下载即用”。大家已经习惯了from_pretrained()一行代码加载预训练模型,不会去检查这个模型的权重文件内部是什么。问题在于,Hugging Face 的文件不只是.bin权重,还包括config.json、tokenizer_config.json、vocab.txt、safetensors,以及一些带.py后缀的自定义代码文件。
只要模型作者在权重仓库里放一个custom_model.py,代码里写一段恶意逻辑,再配合trust_remote_code=True加载配置,就有可能在本地环境中执行任意 Python 代码。这段代码会在你毫无感知的情况下运行,甚至可以读取环境变量、下载内网文件、植入后门。
2.2 反序列化是重灾区
PyTorch 的torch.load()默认不做安全校验。标准模型权重大多以 pickle 格式序列化,而 pickle 在设计上允许“反序列化时执行任意代码”。很多老教程、旧项目、第三方代码库都在用torch.load(),包括 2024 年之后仍有大量项目没有切换到safetensors。
这句话的意思是:一个看起来只是“模型参数”的文件,其实可以附带__reduce__魔术方法,在反序列化阶段触发eval、exec、os.system。更隐蔽的恶意文件会把自己包装成规范模型结构,只在特定条件下触发攻击,普通排查很难发现。
2.3 工具链迁移带来新空窗
热词里反复出现一条 warning:
warning: `huggingface-cli` is deprecated and no longer works. use `hf` instead这说明 Hugging Face 正在把命令行工具从huggingface-cli迁移到hf。工具链迁移本身就是典型攻击窗口:旧命令失效,新命令不熟悉,用户经常搜索“如何下载模型”,攻击者可以做高仿文档、高仿依赖包、高仿安装脚本。如果团队内部还有老脚本依赖huggingface-cli,就会出现“命令报错 → 网上搜教程 → 下载一个伪装包 → 中招”的路径。
这里要做两手准备:一是把项目脚本升级到新的hf命令;二是升级过程中只从官方渠道获取命令和依赖,不要盲目复制第三方博客里的安装命令。
2.4 平台与外部攻击面叠加
除了恶意模型文件,Hugging Face 平台本身也可能遭受攻击。搜索热词里出现了大量 Web 攻击内容,例如 DDoS、XSS、SQL 注入、服务路径劫持。这提醒我们:任何 Web 平台都可能被攻击者利用,而托管海量模型文件的平台一旦被攻破,攻击者可以直接替换热门模型权重,形成大规模供应链污染。
这个情况对本地部署用户和在线调用用户都有影响:
- 本地部署用户从公共平台拉取模型,可能获取到被替换或污染的文件。
- 在线调用用户如果平台本身被攻击,返回的模型响应、推理结果都可能不可信。
- 镜像站和加速下载站点更危险,很多来源不明的“国内加速地址”可能直接改写模型文件。
3. 主要攻击类型与手法分析
3.1 恶意 pickle 模型文件
这是最传统的攻击方式,也是影响面最大的一类。攻击者上传一个小型模型,权重文件里嵌入恶意 pickle 载荷。受害者使用torch.load()加载时,恶意代码直接执行。
常见载荷动作包括:
- 读取
~/.ssh/id_rsa、环境变量、云厂商密钥。 - 向指定服务器发送 HTTP 请求,外传数据。
- 写入定时任务或启动脚本,实现持久化。
- 在受害机器上下载并运行挖矿程序。
这种攻击很容易混入“小型模型”“练习用模型”“微调权重”中,尤其是在 Hugging Face 上模型数量多、审核压力大的情况下。
3.2trust_remote_code滥用
Hugging Face 的from_pretrained()支持加载远程代码来自定义模型结构。这是一个方便功能,也是一个危险开关。模型仓库里的configuration_xxx.py、modeling_xxx.py、tokenization_xxx.py都可能被执行。
如果作者在代码里写入恶意逻辑,并且用户设置:
model = AutoModel.from_pretrained("attacker/evil-model", trust_remote_code=True)那攻击者就获得了直接代码执行能力。更隐蔽的是,代码不会在一开始表现出恶意,而是根据时间、环境变量、IP 段判断是否执行,让沙箱检测更难生效。
3.3 safetensors 绕过技巧
很多安全建议会说“不要用 pickle,改用 safetensors”。这个建议是对的,但攻击者也会变通。safetensors 格式不是万能的,常见绕过思路包括:
- 模型仓库同时提供
.bin和.safetensors,引导用户使用不安全版本。 - 自定义代码通过
trust_remote_code加载 safetensors 后,在预处理或后处理阶段执行恶意逻辑。 - 利用模型配置文件中的自定义 layer,在 forward 阶段执行 shell 命令。
所以,safetensors 可以解决“反序列化执行代码”的问题,但不能解决“模型代码本身有害”的问题。
3.4 数据集投毒
Hugging Face 不止有模型,还有海量数据集。攻击者可以上传一个“看起来正常”的数据集,里面某些样本包含恶意指令或错误标注,使用者在微调模型时,模型会学到恶意行为或后门模式。
这个攻击最隐蔽的地方在于:不直接攻击机器,而是污染模型质量,后续所有使用这个模型的下游任务都会受影响。比如一个命名实体识别模型被投毒,生产环境里可能跳过某些实体的识别;一个代码补全模型被投毒,可能在某些特定 token 后生成不安全代码。
批量训练场景下,数据集投毒会扩散到整个训练任务,而且很难通过普通测试发现。
3.5 依赖包与安装脚本投毒
模型加载不仅仅是 HF 平台内部的事。很多项目会先pip install一些依赖,或者运行setup.py。攻击者可以模仿热门项目名称做“相似包”,或者诱导用户安装不安全的本地依赖。
这类攻击在模型训练环境里特别危险,因为训练镜像通常具备较高权限,能访问 GPU 节点、存储系统、代码仓库。一次依赖投毒,等于把整个训练集群的控制权交出去。
3.6 镜像站与第三方加速服务劫持
国内很多用户受网络条件影响,会使用镜像站或第三方加速服务下载模型。这些服务如果由不可信方维护,就可能对模型文件做手脚。所谓“服务路径劫持”攻击,就是攻击者通过对镜像服务器的路径进行篡改,让用户下载到伪造文件。
更稳妥的做法是使用官方平台或受信任的内部缓存,并且对下载文件做哈希校验。如果必须用镜像,至少要记录文件 SHA256,定期和官方源对比。
4. 真实危害场景分析
4.1 开发机与研究员的电脑
研究员在本地跑实验,经常从 HF 下载各种 SOTA 模型。一旦加载恶意模型,个人电脑上的数据、密钥、论文资料都可能泄露。攻击者还可以利用个人电脑作为跳板,进入公司内网。
4.2 训练集群与 GPU 节点
GPU 节点通常具有强大的计算能力和较高的内网权限。恶意模型在训练节点上运行时,可能被用来挖矿、窃取训练数据、篡改模型权重,甚至把节点变成内网攻击源。这里的影响不只是单台机器,而是整个训练基础设施。
4.3 CI/CD 流水线与模型发布链
现代 AI 团队会把模型训练、评估、打包、发布都做成流水线。如果流水线里任何一个环节触发恶意代码,攻击者就能控制发布产物。想象一下:你训练好的模型被替换成了带后门的版本,并发布到生产环境,后续所有推理结果都不再可信。
4.4 推理服务和批量任务
批量推理脚本通常会读取一个模型清单,然后逐个加载和推理。如果某个模型是恶意的,批量任务会在整个队列里反复执行恶意代码,攻击影响被放大。这里我们不只是讨论负载问题,而是讨论“批量加载不可信模型”本身就是一个高风险操作。
4.5 API 服务与下游产品
即使你只调用第三方 API,同样需要考虑供应链风险。如果上游模型被投毒,API 返回结果可能被故意篡改。对于内容审核、代码生成、医疗文本分析等关键领域,模型输出的可信度直接关系到业务安全。
5. 检测思路与代码示例
注意,下面内容只用于安全防御与检测,不提供攻击利用代码。如果你要做测试,务必在隔离环境、有授权的靶机中进行,不要对真实网络和平台做未授权操作。
5.1 检查 pickle 文件的可疑操作
如果你的项目还在用torch.load(),可以先写一个简单的扫描脚本,检查待加载文件在反序列化过程中是否请求了危险函数。这里给出一个防御侧检测思路:
import pickle import io import sys class AuditUnpickler(pickle.Unpickler): def find_class(self, module, name): # 允许列表机制,只放行安全的数学/张量相关类 allow_list = [ ("torch", "Tensor"), ("torch._utils", "_rebuild_tensor_v2"), ("numpy.core.multiarray", "_reconstruct"), ] if (module, name) in allow_list: return super().find_class(module, name) raise pickle.UnpicklingError( f"blocked: {module}.{name}" ) def audit_pickle(path): with open(path, "rb") as f: try: AuditUnpickler(f).load() print(f"[OK] {path} 未发现危险反序列化调用") except pickle.UnpicklingError as e: print(f"[BLOCKED] {path}: {e}") except Exception as e: print(f"[ERROR] {path}: {e}") # 替换为实际文件路径 audit_pickle("./models/example_model.bin")这个脚本不是为了解决所有问题,而是给你一个观察点:模型加载过程中到底触发了哪些类。如果日志里出现os.system、subprocess、eval、exec,一定要立即终止。
5.2 扫描模型目录中的危险字符串
可以对模型目录做静态扫描,寻找常见的危险调用。注意,这只是快速筛查,不能当成绝对判断标准。
grep -rE "os\.system|subprocess|eval\(|exec\(|base64|curl |wget |socket" \ ./downloaded_model_dir \ --include="*.py" \ --include="*.txt" \ --include="*.json"如果扫描结果里有大量可疑调用,再手动确认相关文件。
5.3 强制使用 safetensors 加载
在实际推理代码中,优先使用safetensors,避免torch.load()。示例:
from safetensors.torch import load_file # 优先从 safetensors 加载 weights_path = "./models/model.safetensors" weights = load_file(weights_path, device="cpu") print("加载完成,张量数量:", len(weights))如果模型仓库同时提供.bin和.safetensors,优先选择后者,并且不要轻易设置trust_remote_code=True。
5.4 检查配置文件和代码文件
下载完模型后,不要急着加载,先查看目录结构:
# 查看模型目录内容 find ./downloaded_model_dir -type f | head -50 # 列出所有 Python 文件 find ./downloaded_model_dir -name "*.py" -type f对每个.py文件做人工审查,尤其是modeling_*.py、configuration_*.py、custom_*.py。一个小技巧:搜索文件里有没有requests、socket、urllib、subprocess、open('/etc/'等敏感调用。
6. 防御与加固方案
6.1 模型加载策略
- 优先使用
safetensors格式,避免 pickle 反序列化。 - 除非完全信任模型来源,否则不要设置
trust_remote_code=True。 - 如果必须使用远程代码,先下载到本地并人工审查,再加载。
- 对常用模型建立白名单,只允许通过白名单加载。
6.2 进程隔离与最小权限
恶意代码能造成多大影响,取决于它运行时的权限。最基本的加固是让模型加载和推理在低权限账号、独立容器或独立进程中运行。
# 示例:创建低权限用户运行推理服务 sudo useradd --system --no-create-home --shell /usr/sbin/nologin model_runner sudo -u model_runner python run_inference.py --model ./models在容器环境里,要避免挂载宿主机的 SSH 密钥、云凭证、Docker Socket。训练任务和推理任务都应该使用专用服务账号,并定期轮换 token。
6.3 网络控制
模型加载和推理进程不应该拥有完整的出网权限。如果你使用容器,可以加网络策略,只允许访问白名单域名,阻止进程向任意服务器发送数据。这能显著降低数据外传风险。
Kubernetes 环境可以使用 NetworkPolicy,Docker 可以使用自定义 bridge 网络或防火墙规则。团队内部如果搭建了模型缓存服务,可以让推理节点只访问内部缓存,不直接访问公网。
6.4 哈希校验与供应链记录
对于团队常用的模型,建议把版本、URL、SHA256 记录到一个清单文件里,部署脚本先校验哈希再加载。
{ "model_id": "org/qwen-7b-chat", "revision": "a1b2c3d4", "file": "model.safetensors", "sha256": "0000000000000000000000000000000000000000000000000000000000000000" }校验代码示例:
# 下载后校验 echo "000... output/model.safetensors" | sha256sum -c -如果哈希不一致,立即停止流程并告警。
6.5 离线私有仓库
对生产环境来说,最稳妥的方案不是直接依赖公有平台,而是搭建内部模型仓库。团队从官方源下载一次模型,人工审核后放到内部 Artifactory 或 OSS 上,之后所有节点从内部仓库拉取。这样即使平台被攻破,影响也有限。
6.6 注意hfCLI 的迁移
如果你所在的团队还在使用huggingface-cli,建议尽快完成迁移。新的命令行是hf,老命令已经 deprecate 且不可用。
# 查看当前 hf 版本 hf --version # 下载模型示例(注意目录和仓库名按实际替换) hf download org/model_name --local-dir ./models迁移过程中要注意:所有安装依赖只从官方 PyPI 或官方 GitHub 获取,不要使用来路不明的脚本。如果你在搜索解决方案时看到让执行pip install某个不明包的教程,先核对包名和来源。
6.7 API 调用安全
如果你只是调用平台的 Inference API,也要注意几点:
- 不要在代码或日志里明文写入 API Token。
- 使用专用 Token,并设置最小权限范围。
- 对 API 返回结果做合法性校验,不要把返回内容直接作为代码执行。
- 监控 API 调用量和异常响应,防止 Token 被滥用。
7. 团队层最佳实践
7.1 建立模型准入流程
团队内部不应该是“谁想下载模型就下载”。建议建立模型准入清单,任何新模型进入团队前必须经过安全评估。评估内容包括模型来源、文件格式、代码文件、许可证、依赖项。
7.2 训练与推理环境隔离
训练环境、推理环境、开发环境最好彼此隔离。训练任务访问的模型和数据,不需要出现在生产推理环境里。最小权限原则在 ML 场景同样适用。
7.3 审计与日志
所有模型相关操作都要有日志:谁下载了什么模型、从哪个地址、用了什么参数、有没有触发异常。日志要集中收集,并且定期审计。很多攻击不是一次性完成,而是长期潜伏,只有日志才能发现问题。
7.4 应急响应预案
准备一个模型安全应急响应流程:
- 发现可疑加载行为或异常出网请求,立即切断模型进程网络。
- 保留现场,保存模型文件、进程快照和日志。
- 检查是否在其他节点复现,确认影响范围。
- 撤销相关 Token、密钥,检查持久化行为。
- 清理恶意文件,更新黑名单和准入清单。
- 复盘攻击路径,补安全策略。
7.5 安全意识培训
普通研发可能不知道torch.load()的风险,也不知道trust_remote_code=True意味着什么。安全团队应该给 AI 工程团队做一次专题培训,用最小演示展示恶意模型可以读取环境变量、执行命令、外传文件。只有意识到问题真实存在,才会在日常代码里加入安全习惯。
8. 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 加载模型时执行了奇怪命令 | pickle 反序列化漏洞 | 使用审计 Unpickler 扫描文件 | 改用 safetensors,删除可疑模型 |
设置了trust_remote_code=True后文件被篡改 | 远程代码被投毒 | 下载代码文件并人工审查 | 不开启该选项,使用本地审查后的副本 |
| 模型加载后出现大量外联请求 | 恶意代码外传数据 | 查看网络连接日志、审计进程行为 | 断开网络,隔离进程,检查异常代码 |
huggingface-cli命令报错 | 旧命令已废弃 | 升级到hf命令 | 使用hf download替代旧命令 |
| 模型文件哈希频繁变化 | 镜像站劫持或下载不完整 | 对比官方哈希 | 从官方源下载,使用内部缓存 |
| 训练时 loss 异常但无报错 | 数据集投毒或权重篡改 | 对比基准模型输出 | 清理数据集,重新校验模型 |
| API Token 泄露 | 代码仓库泄露或日志误输出 | 检查仓库历史和日志 | 撤销 Token,启用权限最小化 |
| 批量任务加载多个模型时集体卡住 | 某个模型文件触发恶意逻辑 | 逐个加载排查,观察资源占用 | 先用白名单小范围测试 |
| 推理结果在特定输入下不稳定 | 模型可能被植入后门 | 测试触发词和异常输入 | 回滚到已知安全版本 |
| 内网节点出现横向移动痕迹 | 恶意模型作为跳板 | 检查横向流量、进程父子关系 | 隔离节点,强化内网分段 |
9. 加固后的轻量加载示例
最后给一个安全的推理加载模板,适合批量任务和接口服务使用。
import hashlib from pathlib import Path from safetensors.torch import load_file EXPECTED_HASH = "0000000000000000000000000000000000000000000000000000000000000000" # 替换为真实哈希 def verify_sha256(file_path, expected_hash): sha256 = hashlib.sha256() with open(file_path, "rb") as f: for block in iter(lambda: f.read(4096), b""): sha256.update(block) return sha256.hexdigest() == expected_hash def load_model_safe(model_path): model_path = Path(model_path) if not model_path.exists(): raise FileNotFoundError(f"model not found: {model_path}") # 优先加载 safetensors,并通过哈希校验 safetensors_path = model_path / "model.safetensors" if safetensors_path.exists(): if verify_sha256(safetensors_path, EXPECTED_HASH): weights = load_file(str(safetensors_path), device="cpu") print(f"[OK] loaded {len(weights)} tensors from {safetensors_path}") return weights else: raise ValueError("safetensors hash mismatch, refuse to load") # 拒绝直接加载 pickle 格式 raise ValueError("only safetensors files are allowed in this environment")这段代码的逻辑不复杂:先校验哈希,再加载 safetensors,遇到 pickle 格式直接拒绝。如果你在公司内部维护模型服务,这个模式足够作为起步方案。
10. 总结与下一步
HF 攻击真正危险的地方,不是某一个攻击手法多高级,而是 AI 工程社区已经形成了“模型即黑盒、下载即信任”的使用习惯。当攻击者把恶意代码伪装成模型权重、数据集、配置文件或第三方依赖时,受害者几乎没有任何感知地交出执行权限。再加上huggingface-cli到hf的工具链迁移,新命令、新文档、新依赖会带来更多可乘之机。
如果你现在只做一件事,那就是:把所有非必要场景里的torch.load()替换成safetensors,关闭不必要的trust_remote_code=True,并且给团队维护一个模型白名单和哈希校验清单。如果你还负责 MLOps 基础设施,建议进一步做进程隔离、网络控制和日志审计。
下一步可以重点验证三个方向:
- 选一个团队常用模型,做一次完整的文件结构审查,确认有没有可疑
.py文件和异常依赖。 - 把推理代码升级为 safetensors + 哈希校验模式,试跑典型批量和 API 场景。
- 建立模型准入流程,把“下载模型”从一个开发动作升级为一个有审核、有记录、有回滚预案的工程流程。
HF 生态给 AI 落地带来了巨大便利,但便利不能以牺牲安全边界为代价。模型文件的信任要建立在校验上,而不是建立在习惯上。这篇文章里的检测代码和加固方案可以直接落到项目里,强烈建议收藏保存。