
在 AI 应用开发里Hugging FaceHF已经成为很多团队默认的模型来源。但越是默认安全问题越容易被低估。最近使用旧脚本下载模型时会得到一条看起来只是工具升级的警告warning: huggingface-cli is deprecated and no longer works. use hf instead。它提醒我们 HF 生态正在快速变化旧脚本不会自动变安全旧模型文件也不会因为平台口碑好就天然可信。真正值得警惕的是围绕 HF 模型供应链的攻击面恶意权重文件、Pickle 反序列化执行、数据集投毒、伪造仓库、令牌泄露、平台侧流量滥用。这些问题单独看都不算新但组合在一起覆盖了下载、加载、训练、部署和运维整个 AI 开发链路。本文会从 HF 在开发链路中的位置开始讲清楚攻击面为何“比预想严重”再给出本地开发、CI、生产环境可落地的安全方案和排查路径。1. 先理解 HF 在 AI 开发生命周期中的真实位置1.1 从huggingface-cli弃用警告看生态变化很多老项目里的下载命令仍然是这样的huggingface-cli download bert-base-uncased在较新版本的huggingface_hub中这条命令会输出版本迁移警告并直接停止工作Warning: huggingface-cli is deprecated and no longer works. Use hf instead.正确的迁移方式是使用新的hf命令pip install -U huggingface_hub[cli] hf auth login hf download bert-base-uncased --local-dir ./models/bert-base-uncased这里要理解的不只是命令改名。旧的huggingface-cli是历史遗留入口新命令hf统一了模型、数据集、Space 的交互方式。很多 CI 脚本仍然在调用旧命令一旦上游版本升级下载步骤会静默失败或直接中断。更麻烦的是为了绕过版本报错有人会去修改 PATH、复制旧脚本、安装历史版本这些做法反而会把依赖环境引入不可控状态。因此CLI 迁移不是“顺手改一行命令”而是模型供应链治理的一部分。另一个容易被忽略的点是新 CLI 的大量参数也改变了模型文件的落地方式。比如--local-dir会把文件解包到指定目录--cache-dir控制缓存位置--revision能固定到某个 commit。生产环境如果不指定 revision每次下载都可能得到不同版本安全审计也无从谈起。1.2 HF 生态为什么让“加载模型”变成“执行代码”很多人把模型文件理解成普通二进制数据认为下载下来、保存好、再传给模型库解析就完成了。这个理解在 LLM 时代非常危险。Hugging Face 托管的不仅仅是权重文件还有数据集、模型卡片、tokenizer 配置、Spaces 应用和推理 API。transformers的from_pretrained在加载模型时会读取目录中的 config、tokenizer、权重等多个文件并调用对应解析逻辑。其中权重文件如果是 PyTorch 的.pt、.pth、.bin格式默认的torch.load底层依赖 Python 的pickle。而pickle反序列化时会把字节流还原成 Python 对象这个过程可能执行对象里的构造逻辑。简化理解就是一个.pt文件看起来是“模型权重”其实它可以被制作成“一串带有执行逻辑的序列化对象”。只要加载它的进程有权限它就能在当前用户上下文中执行任意操作。这也是 HF 攻击比传统 Web 攻击更隐蔽的原因。Web 攻击通常发生在请求入口有参数校验、WAF、日志等环节。模型文件攻击发生在训练或推理的启动阶段很多团队完全没有把“加载模型”当成“执行外部代码”来对待。1.3 攻击面清单不只是“模型有毒”在落地防护前先用一张表把 HF 生态的主要攻击面梳理清楚风险类别典型场景影响环节恶意权重文件模型文件通过 pickle 逻辑执行代码加载与推理数据集投毒微调样本中注入后门触发词训练与微调伪造仓库仿冒官方 repo id 或组织名下载与安装依赖链漏洞transformers、torch、tokenizer 存在漏洞安装与运行平台侧滥用大流量下载、恶意模型卡、令牌泄露平台与账户安全凭据泄露日志中出现 HF token整个组织这个清单说明不能只检查“模型文件有没有毒”而要把下载、加载、训练、部署、运行五个环节都纳入安全设计。任何一环失守都可能让模型供应链成为攻击入口。2. 为什么 Pickle 反序列化是 HF 攻击的核心2.1 权重文件如何变成攻击载体PyTorch 官方文档一直在提醒用户torch.load加载的是 pickle 文件因此不要加载不可信来源的数据。看一段最常见的反模式import torch # 反模式直接加载不可信 checkpoint model_weights torch.load(./model.pt)这段代码的问题在于torch.load会把文件中的对象还原出来而还原过程中可能触发__reduce__等特殊方法。攻击者可以把命令执行逻辑塞进权重文件让受害者以为自己在“加载模型”实际已经在当前进程中执行了外部代码。这不是理论风险而是真实存在的供应链攻击方式。一个恶意模型被发布到 HF 后会被下游开发者通过from_pretrained自动下载并加载。只要模型目录里包含经过构造的.pt文件攻击者就能在受害者的服务器上获得代码执行能力。更让人头疼的是恶意逻辑不一定写在明显位置。它可以藏在某个辅助文件、tokenizer 文件或者自定义 config 解析路径里普通 review 很难通过肉眼发现。2.2 safetensors 能解决什么不能解决什么为了缓解 pickle 风险Hugging Face 社区推动使用safetensors格式。它只保存张量的布局和数据不包含可执行逻辑因此不会在加载时执行pickle还原。安全的加载方式from safetensors.torch import load_file weights load_file(./model.safetensors)使用AutoModel时可以显式指定优先使用 safetensorsfrom transformers import AutoModel model AutoModel.from_pretrained( ./models/bert-base-uncased, local_files_onlyTrue, use_safetensorsTrue, )这里local_files_onlyTrue表示只从本地目录加载不再通过网络拉取额外文件避免加载过程中“偷偷”下载更多内容。use_safetensorsTrue则要求该目录必须存在 safetensors 权重否则会报错。但要注意safetensors 只能解决“反序列化执行代码”这一类风险不能解决“权重本身被投毒”的问题。攻击者依然可以把一个带后门效果的模型转换成 safetensors 格式加载过程不会报错之后模型会在特定触发词下输出错误结果。因此safetensors 是安全基线不等于模型天然可信。2.3 数据投毒模型行为层面的“软攻击”除了权重文件Hugging Face 上还有大量数据集。微调之前通常只检查数据规模和格式很少验证数据内容是否可信。攻击者可以把恶意样本混入开源数据集在训练后留下一个“后门模式”正常输入表现良好遇到特定触发词时输出恶意或错误结果。这类攻击不需要代码执行但会造成模型行为的系统性偏差。防御思路主要有三层数据源固定只使用经过团队确认和审批的数据集固定 commit 或版本。数据校验下载后计算哈希并检查 JSONL、CSV 等文件的字段结构。行为验证在验证集之外额外准备一组触发词和边界样本确认模型行为没有异常。一个最简单的数据集哈希校验示例import hashlib from pathlib import Path def sha256_file(path): h hashlib.sha256() with Path(path).open(rb) as f: for chunk in iter(lambda: f.read(1024 * 1024), b): h.update(chunk) return h.hexdigest() expected your_expected_sha256_hex actual sha256_file(./data/train.jsonl) if actual ! expected: raise SystemExit(数据集哈希不一致停止训练)这段代码解决的是“下载后的文件是否与官方一致”不能解决“官方数据集本身是否干净”。所以更完整的流程是先审数据来源再校验哈希最后在隔离环境里做小样本训练验证。3. 在本地开发与 CI 中安全使用 HF 生态3.1 把旧 CLI 迁移到hf并锁定依赖本地开发和 CI 是接触 HF 最频繁的两个环境。首先要做的不是写更多防护代码而是把依赖和命令统一。先升级 CLIpip install -U huggingface_hub[cli]检查版本hf --version如果项目里还有huggingface-cli需要全局搜索并替换成hf。除了命令替换还要把依赖版本锁定。因为torch、transformers、safetensors、huggingface_hub之间存在版本耦合很多安全更新都是在新版本中修复的。常见依赖锁定文件片段huggingface_hub0.23 transformers4.46.3 torch2.6.0 safetensors0.5.2具体版本号要以团队实际验证过的组合为准。不要把transformers写成latest也不要在训练环境里随意升级 torch 大版本否则会出现“安全修复了模型推理结果变了”的兼容问题。hf download的核心参数可以用于标准化下载参数作用使用建议--local-dir指定模型落地目录生产环境固定目录便于审计--revision指定 commit、tag 或分支生产环境必须固定 commit--include按 glob 只下载匹配文件可以排除无关文件--exclude按 glob 排除文件避免下载.bin等 pickle 文件--cache-dir指定 HF 缓存目录学习环境可省略生产环境建议独立目录--force-download强制重新下载只在确认缓存损坏时使用示例hf download bert-base-uncased --revision 1e5a9e0 --local-dir ./models/bert-base-uncased --exclude *.bin这个命令把模型固定到指定 commit并排除.bin权重强制目录中只保留 safetensors 和配置文件。如果仓库没有 safetensors 文件加载时会报错这样反而能提前暴露问题。3.2 校验仓库身份与文件哈希下载前要确认 repo id 是否来自可信组织。bert-base-uncased是官方仓库而类似some-user/bert-base-uncased的伪装仓库在命名上很容易让人放松警惕。不要以 star 数和下载量作为唯一判断标准要检查组织名称、model card、文件列表和发布时间。如果 huggingface_hub 支持文件元数据查询可以用代码读取 LFS 文件的 sha256from huggingface_hub import HfApi api HfApi() info api.model_info(bert-base-uncased, files_metadataTrue) for sibling in info.siblings: if sibling.rfilename.endswith(.safetensors): print(sibling.rfilename, sibling.lfs.get(sha256))下载完成后在本地计算哈希并做对比sha256sum ./models/bert-base-uncased/model.safetensors校验通过后再进入加载流程。这个步骤看起来繁琐但生产环境一旦遇到“模型文件被平台端污染”或“内部存储被人替换”哈希是唯一能确认文件完整性的手段。3.3 用安全加载方式保护推理代码在推理代码中要尽量避免直接执行裸的torch.load。推荐组合是local_files_onlyTrue、use_safetensorsTruefrom transformers import AutoTokenizer, AutoModel local_dir ./models/bert-base-uncased tokenizer AutoTokenizer.from_pretrained(local_dir, local_files_onlyTrue) model AutoModel.from_pretrained( local_dir, local_files_onlyTrue, use_safetensorsTrue, )如果项目确实还需要加载.pt文件并且模型来自可信团队可以使用weights_onlyTrue限制反序列化范围import torch state torch.load(./checkpoint.pt, weights_onlyTrue)weights_onlyTrue在较新的 PyTorch 中会限制加载内容只能是张量和基本对象降低 pickle 执行任意代码的风险。但它不是万能的某些包含自定义类的 checkpoint 会直接报错。遇到这种情况不要简单地去掉weights_onlyTrue而应该评估该 checkpoint 是否真的需要自定义类或者想办法转换成 safetensors 格式。注意weights_onlyTrue解决的是“反序列化代码执行”问题不能解决“模型权重本身带后门”问题。加载前的来源确认和哈希校验仍然不能省。3.4 令牌与下载行为的受控管理很多团队把 HF token 直接放在.env或 Docker 容器镜像中认为环境变量不算泄露。实际上环境变量可能出现在日志、子进程列表、错误上报和 CI 输出中。更安全的方式是使用密钥管理服务在运行时通过临时凭据注入。登录交互hf auth login在 CI 中不要用--token明文参数因为命令行参数会被记录到 shell history。可以将令牌放入 CI 的 secret 环境变量中并让huggingface_hub自动读取export HUGGING_FACE_HUB_TOKENyour_short_lived_token令牌权限要遵循最小化原则。下载私有模型只需要只读权限不要给上传权限。训练服务和推理服务最好使用不同的令牌出现泄露时可以单独吊销不会影响整个账号。生产环境还应该把“从公网下载模型”改成“从内部制品库获取模型”。先在可信环境下载并校验好模型再上传到内部对象存储或制品库业务服务器只从内部地址读取文件。这样既能减少对外网流量的依赖也能把模型来源收敛到一个可控入口。4. 怀疑模型供应链已经出问题时的排查链路4.1 先判断哪些现象属于供应链异常模型供应链出问题时的现象不一定像 SQL 注入那样明显。常见的异常信号包括加载模型后进程出现未预期的网络外联。CPU 或内存使用量突然上涨且与推理并发量不匹配。同一份模型在固定 commit 下输出结果多次变化。缓存目录中出现了发布单之外的 repo 文件夹。日志中出现未知的import、eval、exec或模型文件之外的可执行文件。用旧huggingface-cli下载时报错但有人擅自修改了命令行为来“绕过”。这些现象不一定都是攻击但需要按供应链事件处理先隔离、再取证而不是继续重试任务。4.2 按顺序隔离、取证、定位排查顺序很重要。第一步是隔离现场第二步是保存证据第三步才是定位根因。一旦怀疑模型文件不可信先把容器从业务网络隔离停止不必要的定时任务和对外服务。不要立刻删除模型文件因为删除会丢失取证线索。然后查看进程和网络连接ps auxwww | grep -E python|huggingface|hf sudo ss -tunp | grep python重点看是否有异常的目标地址和连接状态。如果模型加载进程在没有任何推理请求时主动外联这是高风险信号。再看 HF 缓存目录find ~/.cache/huggingface -type f -mtime -1 -ls正常情况应该只有最近发布的模型文件。如果出现陌生仓库、临时脚本、可执行文件需要进一步检查。4.3 文件哈希与加载栈核查确认网络和进程之后进入文件级排查。排查阶段检查点命令或工具预期结果隔离现场停止外部流量保留文件和日志网络策略、暂停定时任务现场不被污染进程检查确认加载模型的实际进程ps auxwww进程身份与发布单一致网络检查查看进程外联目标ss -tunp无未预期外联缓存检查查看缓存目录变动find ~/.cache/huggingface -type f -mtime -1只有计划内文件哈希校验比对模型文件哈希sha256sum model.safetensors与 HfApi 或管理员提供值一致加载栈检查查看进程调用栈py-spy dump --pid pid堆栈中无可疑自定义对象执行文件内容检查搜索二进制中的执行特征strings model.ptgrep -E evalstrings检查会存在误报因为正常模型文件里也可能出现request等词汇。它只能作为筛选线索不能作为最终结论。确认存在恶意行为后保留完整日志、进程快照、模型文件哈希和下载记录然后向模型平台和内部安全团队报告。不要因为模型能加载、推理结果正常就认为没有风险很多恶意模型只在特定条件下才会触发。如果怀疑模型被投毒第一步不是继续执行而是把进程从业务网络隔离保留现场。5. 生产环境模型服务的安全基线5.1 运行时隔离不要把宿主机直接暴露给未知模型生产环境跑模型之前要默认模型文件可能不可信。最好的防御是让模型加载进程没有权限做危险操作。在 Kubernetes 中至少要为推理 Pod 配置非 root、只读根文件系统和最小能力集securityContext: runAsNonRoot: true readOnlyRootFilesystem: true capabilities: drop: [ALL]