
OpenAI 发布 Hugging Face 事件技术报告是近期模型供应链安全话题里最值得关注的材料之一。这里的核心关键词不是某一家公司的名字而是“Hugging Face 事件”和“技术报告”组合起来的分析方式。Hugging Face 是当前 AI 开发者获取预训练模型、数据集和推理脚本的主要平台任何一个托管仓库出现恶意代码影响的都不只是单个下载者而可能沿着模型依赖链条扩散到下游应用。本文不搬运报告原稿而是站在工程实践角度拆解这类事件报告通常披露了什么、恶意模型文件可能通过哪几条路径进入机器、开发者应该用什么命令和工具完成自查以及生产环境如何建立可执行的模型安全基线。1. 先理解Hugging Face 模型供应链事件为什么值得关注Hugging Face 在 AI 工程中的地位很像 npm 之于前端、PyPI 之于 Python但它比普通包管理平台更特殊。普通依赖包通常只是代码而 Hugging Face 仓库里还包含几百 MB 甚至几十 GB 的模型权重、tokenizer 文件、预处理脚本、配置文件。下载者拿到这些文件后通常不会先审查完整内容而是直接用transformers、diffusers、torch.load等方式加载。这个行为本身就等于信任了仓库作者的全部文件。开发者从 Hugging Face 拉取模型的标准流程大致是先在网页搜索合适模型然后复制snapshot_download或from_pretrained代码在本地环境运行随后看到权重下载完成直接加载推理。整个链路里缺少“文件真实性验证”和“代码执行前审计”两个环节。一旦托管仓库被投毒恶意代码就可能在用户机器上执行读取环境变量、发送 token、篡改本地文件甚至横向扩散到同一网络下其他服务。这也是 OpenAI 发布的事件技术报告值得读的原因它把一次发生在主流模型托管平台上的安全事件拆成了时间线、攻击载体、影响范围、缓解措施和后续改进。普通开发者不需要把报告当新闻看而是应该把它当作一份“模型供应链安全自查手册”来用。1.1 Hugging Face 在模型供应链中的位置Hugging Face Hub 的核心功能包括三块模型仓库、数据集仓库和 Spaces 应用。模型仓库通过 Git 管理文件同时支持 LFSLarge File Storage存放超过 10MB 的大文件。开发者可以通过git clone、huggingface-cli或huggingface_hub库下载仓库内容。从安全角度看这个平台有四个特点任何注册用户都可以创建仓库并发布模型审核机制以事后举报和主动扫描为主。仓库文件名是任意的.pt、.pkl、.bin、.safetensors、.py、.json都可能同时存在。代码和权重混在同一个仓库里下载者无法通过目录名判断哪些文件是纯数据、哪些是可执行内容。仓库支持更新开发者如果固定了 commit hash可以保证后续下载内容不变如果没有固定使用from_pretrained默认可能拉到最新版本这给了仓库作者在不通知使用方的情况下替换文件的机会。这四个特点组合在一起导致 Hugging Face 仓库成为模型投毒攻击的主要阵地。攻击者不需要花精力攻击 transformers 框架只需要上传一个“看起来正常”的模型文件等受害者执行加载逻辑攻击就完成了。1.2 OpenAI 事件技术报告通常包含哪些内容从 OpenAI 公开过的技术报告和行业惯例来看一份完善的模型供应链事件报告至少会覆盖以下模块报告模块说明对普通开发者的价值事件时间线从首次发现异常到完成处置的时间节点用来对照自己的检测和响应速度攻击载体恶意文件类型、分发渠道、触发条件用来确定自查时该扫哪些文件影响范围受影响的仓库、下载量、第三方应用用来判断风险等级检测方法平台扫描规则、恶意代码特征、哈希值可以转化为本地扫描脚本缓解措施下架仓库、撤销 token、阻断下载对应自己的应急处理流程改进计划平台审核策略、文件格式限制、自动扫描升级对应自己的长期安全能力建设阅读报告时不建议只关注“这次事件谁负责”。更有效的做法是提取信息如果我要防御同类事件我需要检测哪些文件类型使用什么工具扫描加载前后需要做哪些隔离token 和凭据应该如何管理。这样一份外部事件报告就能变成内部安全检查清单。1.3 技术报告和学习者之间的关系很多开发者觉得自己只是模型使用者不是平台运营者事件报告和自己没关系。实际上模型供应链攻击的受害者恰恰是使用方。攻击者上传恶意模型到 Hugging Face最终的执行环境是普通开发者的电脑或服务器。你不运营平台但你负责运行加载模型的那台机器。所以阅读 OpenAI 发布 Hugging Face 事件技术报告的正确姿势是把报告里的“平台视角”翻译成“使用者视角”。平台从服务端下架仓库你需要从客户端确认没有加载过旧版本平台撤销用户 token你需要确认自己是否在相关环境里配置过相同的密钥平台增加扫描策略你需要在自己的下载流程里加入同样的校验环节。下文的各个章节就是围绕这个翻译过程展开的。2. 事件背后的技术基础模型文件是怎么被利用的模型文件的攻击方式并不神秘关键在“反序列化”和“代码执行”两个概念上。理解这一点才能明白为什么safetensors被反复推荐为什么扫描工具会盯着torch.load和 pickle 文件不放。2.1 模型权重与代码的边界模型权重本质上是一组浮点数数组对应神经网络的参数。很多框架会把权重保存成二进制文件但为了便于存储额外信息开发者经常会把模型结构、优化器状态、训练步数等一并打包。PyTorch 传统上直接使用 Python 自带的 pickle 序列化格式保存文件后缀可能是.pt、.pkl或.bin。pickle 的问题是它是 Python 对象序列化协议不仅支持数字、列表、字典还支持“任意 Python 对象的引用”。反序列化时pickle 会执行文件里记录的操作码如果文件里包含一个自定义类反序列化过程就可能触发这个类的代码。因此torch.load(model.pt)在加载权重时实际上是在执行一个不可信的 pickle 文件。文件格式序列化方式是否存在代码执行风险.safetensors自定义二进制格式只存张量无代码执行能力.pt/.pklpickle有取决于内容.bin可能是 pickle也可能是纯权重需要检查实际格式.npz/.npyNumPy 二进制格式一般无代码执行但要注意解析漏洞.gguf/.onnx结构化权重格式相对安全但也要验证来源这也是为什么事件报告和技术社区都强烈建议优先使用.safetensors格式。safetensors 的设计目标就是解决 pickle 反序列化导致的任意代码执行问题它只读取张量数据不执行任何 Python 对象构造逻辑。2.2 攻击面一pickle 反序列化恶意 pickle 文件的常见逻辑是在反序列化过程中调用os.system、subprocess.Popen、socket等操作或者读取环境变量并发送到远端服务器。一个典型但危险的加载方式是这样的import torch # 这种做法等同于执行 model.pt 里保存的任意 pickle 指令 model torch.load(model.pt, map_locationcpu)如果model.pt来自不可信仓库这段代码就可能在当前进程权限下执行任意命令。攻击者可以把命令隐藏在torch.save的 pickle 数据里当受害者执行torch.load时自动触发。检测思路很简单不要用torch.load直接加载不可信文件而是先扫描或者改用支持安全加载的库。例如picklescan可以在不执行 pickle 内容的情况下分析文件里引用了哪些 Python 模块和全局变量。pip install picklescan picklescan scan model.pt扫描结果会列出 pickle 中引用的模块、操作码和风险等级。如果看到os.system、eval、exec、socket等出现在模型文件里这个文件就高度可疑。2.3 攻击面二数据集和预处理脚本模型仓库里除了权重文件经常还会有数据集的 CSV、JSONL 文件以及用于数据清洗和预处理的 Python 脚本。transformers的datasets库支持多种本地数据集格式部分格式会执行自定义代码。特别需要注意.py文件。有些开发者会在仓库里放tokenization.py、modeling.py、preprocess.py等脚本使用代码如下from transformers import AutoTokenizer tokenizer AutoTokenizer.from_pretrained(some/repo)from_pretrained在加载 tokenizer 时会先下载仓库里的tokenizer_config.json和vocab.txt再通过tokenizer_class字段实例化对应类。如果仓库作者把自定义 tokenizer 脚本放在仓库里并在配置中引用加载时有可能执行自定义 Python 代码。同理datasets.load_dataset加载自定义脚本数据集时也会执行仓库里的loading script。这类攻击比 pickle 更隐蔽因为文件本身不是.pkl而是一个看起来正常的 Python 文件开发者在代码审查时很容易忽略。2.4 攻击面三依赖混淆和镜像仓库Hugging Face 仓库命名格式是owner/repo_name。攻击者可以注册和热门仓库相似的用户名例如把用户名的字母l改成数字1把o改成0然后上传一个同结构恶意仓库。如果开发者在复制命令时看漏一位就会下载到错误仓库。此外部分开发者为了加快下载速度会配置 Hugging Face 镜像站。镜像站如果缺乏完整校验机制可能返回修改过的文件或者镜像站自身被攻破就会直接分发恶意权重。因此在生产环境使用镜像站时必须额外校验文件哈希并锁定仓库 commit。3. 搭建本地检查环境跑通一个最小扫描闭环在分析任何事件报告之后建议立刻在本地复现一遍“下载文件 - 扫描文件 - 判断风险 - 安全加载”的完整流程。下面用一个最小环境演示所有操作都可以在 Linux 或 macOS 终端完成。3.1 环境准备和依赖安装先准备一个独立的 Python 环境避免把依赖装进系统环境python3 -m venv hf-audit source hf-audit/bin/activate pip install --upgrade pip pip install huggingface_hub picklescan safetensors这里安装三个工具huggingface_hub用于下载 Hub 仓库文件并查询元数据picklescan用于扫描 pickle 文件中的可疑引用safetensors用于验证和转换权重文件。如果后续需要加载模型做对照也可以临时安装torch但建议只在一台隔离的测试机上安装。检查安装结果python -c import huggingface_hub, picklescan, safetensors; print(env ok)应该输出env ok。这一步的重点是保证后续命令都在同一个环境里执行避免因为不同 Python 环境导致扫描结果不一致。3.2 下载目标仓库到隔离目录为了安全不要直接在工作目录下载模型。先创建一个隔离目录并限定权限mkdir -p /tmp/hf-quarantine cd /tmp/hf-quarantine然后使用huggingface_hub下载仓库快照。以某个示例仓库为例但不要在生产环境直接运行python -c from huggingface_hub import snapshot_download snapshot_download( repo_idsome-namespace/some-model, local_dir./downloaded_model, local_dir_use_symlinksFalse, allow_patterns[*.pt, *.pkl, *.bin, *.safetensors, *.py, *.json, *.txt], revisionmain ) 这里用allow_patterns只下载重点关注的文件类型减少无用文件加快扫描。下载时务必指定revision最好锁定到具体的 commit hash而不是main这样后续校验时才有明确的基线。3.3 用 picklescan 扫描本地文件下载完成后进入目录对全部文件执行扫描cd downloaded_model find . -type f \( -name *.pt -o -name *.pkl -o -name *.bin \) -exec picklescan scan {} \;也可以对具体文件执行picklescan scan ./model/pytorch_model.binpicklescan会分析文件是否包含 pickle 流并列出其中的GLOBAL指令。以下是一个可疑输出示例 Scanning ./model/pytorch_model.bin ✳️ Found Python global: posix.system ✳️ Found Python global: subprocess.Popen ✳️ Found Python global: socket.socket Check https://... ⚠️ Scan: 3 suspicious globals看到posix.system、subprocess.Popen、socket.socket出现在模型文件中基本可以判定为恶意文件。正常权重文件不会调用系统命令和网络套接字。注意picklescan的输出格式在不同版本中可能略有差异但可疑全局变量名称通常会直接展示。不要因为版本提示不同而忽略扫描结果。3.4 检查 safetensors 文件的可信度safetensors 文件不包含可执行代码可以更安全地加载但仍需要验证文件是否完整、是否和仓库元数据一致。使用safetensors库读取头信息python -c from safetensors import safe_open with safe_open(./downloaded_model/model.safetensors, frameworkpt, devicecpu) as f: for k in list(f.keys())[:5]: print(k) 如果文件损坏、被截断或格式错误safe_open会抛出异常。这种方法可以在加载前快速判断文件是否可读。不过safetensors 只能避免 pickle 反序列化攻击不能避免仓库里的 Python 脚本攻击。所以扫描流程必须覆盖所有.py文件查看代码内容而不是只扫描权重。4. 深入排查从识别到处置恶意模型文件如果扫描发现了可疑内容不应该只用一句“这个模型有问题”结束。还需要收集证据、确认影响、隔离文件并检查整个运行环境。4.1 查看文件是否匹配平台元数据使用huggingface_hub的list_repo_files和model_info可以查看仓库官方文件列表和提交信息确认本地文件与远端是否一致。python -c from huggingface_hub import HfApi api HfApi() info api.model_info(some-namespace/some-model) print(sha:, info.sha) files api.list_repo_files(some-namespace/some-model) for f in files: print(f) 这一步的核心是确认自己下载的文件是否来自预期 commit。如果本地文件大小与远端不一致可能是下载过程被篡改也可能是本地缓存了旧版本。结合.cache/huggingface目录里的元数据可以查到文件来源。4.2 分析 pickle 文件中的可疑调用对于picklescan标记的可疑文件可以进一步用 Python 解析其 pickle 操作码但不要直接加载执行。以下代码只读取操作码不执行任意代码import pickle with open(./model/pytorch_model.bin, rb) as f: ops pickle._Unpickler(f) ops.encoding latin1 # 这里不调用 load()只演示如何检查文件是否可被正常解析 # 真正的安全分析需要更完整的基础设施不建议在生产环境执行 print(pickle stream start, size:, ops.file_tell() if hasattr(ops, file_tell) else unknown)更稳妥的做法是使用pikepdf或fickling这类专门分析 pickle 的工具。fickling可以把 pickle 文件转换为 AST 和可视化日志pip install fickling fickling model.ptfickling会输出文件中引用的模块、函数、常量和控制流信息。即使不能 100% 还原所有恶意逻辑也能发现明显异常。4.3 隔离并删除可疑文件发现恶意文件后不要直接删除因为后续还需要取证和复盘。先把整个仓库目录移动到隔离区mkdir -p /tmp/hf-quarantine/quarantined mv ./downloaded_model /tmp/hf-quarantine/quarantined/ chmod -R a-w /tmp/hf-quarantine/quarantined/同时统计可疑文件清单写入报告find /tmp/hf-quarantine/quarantined -type f -name *.pkl -o -name *.pt /tmp/hf-quarantine/suspicious_files.txt wc -l /tmp/hf-quarantine/suspicious_files.txt如果确认文件确实恶意再在隔离目录内清除它。清除后需要回收已经被该文件使用过的环境检查~/.cache/huggingface下是否有对应缓存手动清理。检查当前 shell 的HF_TOKEN、OPENAI_API_KEY等环境变量如果模型加载代码有能力读取这些变量就要强制撤销并重新生成。如果有容器或虚拟环境建议销毁重建避免留下隐藏进程或修改过的依赖。4.4 检查依赖库是否被篡改有时候恶意代码并不在模型文件中而是通过模型仓库里的requirements.txt引入一个恶意的依赖包。所以事件响应阶段还要检查 Python 环境pip list --formatfreeze installed_packages.txt pip check pip list --outdated重点对比installed_packages.txt中的包版本和项目锁文件如requirements.txt、pyproject.toml是否一致。如果出现了没有在项目声明中出现的包或者某个包版本被悄悄替换就要回溯安装时间。在 Linux 上可以用auditctl或文件完整性工具监控敏感目录但普通开发者至少应该养成“环境被污染后重建”的习惯而不是尝试在受感染环境里一层层清理。5. 常见问题排查清单实际排查过程中命令输出不会总是像文档里那样干净。下面是几类常见问题以及对应的处理思路。5.1 picklescan 报错 “not a valid pickle”出现这个错误不代表文件安全反而要先确认文件格式。很多.bin文件并不是 pickle而是纯张量数据、JSON 或 safetensors 数据。此时应该用file命令查看文件类型file ./model/pytorch_model.bin如果输出显示data或JSON text说明它可能不是 pickle。这时需要进一步确定加载方式。如果代码里调用torch.load加载一个非 pickle 文件通常会报UnpicklingError。而如果文件是 safetensors框架会自行处理不需要 pickle 扫描。现象可能原因检查方式picklescan 提示 not a valid pickle文件不是 pickle 格式可能是 safetensors/JSON/纯二进制使用file查看类型扫描无异常但torch.load报 UnpicklingError文件被截断或不是完整权重对比仓库文件大小和哈希扫描无异常加载时仍执行了外部命令攻击点可能在.py脚本或数据集脚本而不在权重文件审查所有 Python 脚本的 import 和顶层代码5.2 扫描后没有风险但加载仍然有异常扫描工具并非万能。Pickle 可以构造非常复杂的操作码静态扫描可能漏掉某些只在特定 Python 版本下触发的逻辑。如果运行环境可疑建议在无网络权限的隔离容器里加载观察是否有对外连接请求。用strace -f -e execve,network监听进程行为。加载前后对比环境变量、文件系统、进程列表的变化。如果发现模型加载后会自动创建~/.ssh/id_rsa、访问~/.aws/credentials或执行 curl 请求立即终止进程并隔离网络。5.3 模型文件太大扫描耗时太长大模型动辄几十 GB逐文件扫描 pickle 会非常慢。推荐思路是先列出仓库文件清单只扫描.pt、.pkl、.bin文件。对于.safetensors文件跳过 pickle 扫描直接验证哈希。使用picklescan --max-file-size或者分片扫描但分片可能破坏 pickle 结构结果只能作为参考。如果模型仓库很大建议在 CI 阶段预扫描而不是在线下载时扫描。一个合理的仓库安全检查流程是先通过 API 获取仓库元数据再下载文件在沙箱里执行扫描最后才把文件拷贝到生产环境。这样即使某个文件被恶意更新预处理阶段就能拦下。5.4 使用镜像站下载时文件不一致如果配置了 Hugging Face 镜像站HF_ENDPOINT会指向镜像地址。示例如下export HF_ENDPOINThttps://hf-mirror.com使用镜像站前应确认镜像源是可信的并在下载后对比仓库 commit 哈希和文件 sha256。镜像站可能因为缓存不同步导致文件过期极端情况下也可能提供被替换的文件。因此生产环境建议直接在官方站点下载或者在镜像拉取后执行严格的哈希校验。注意不要在任何不受信任的环境里导出HF_TOKEN。如果必须使用登录态下载使用只读权限的 token并在下载完成后立即撤销。5.5 torch.load 触发可疑行为如果你没有先扫描而是直接执行了torch.load发现进程卡顿、网络连接异常或者文件异常写入首先要做的是断开网络而不是继续调试。随后检查是否有新的进程ps aux | grep python ss -tunap | grep python接着查看当前目录是否生成了新文件ls -la find /tmp -newer ./model 2/dev/null如果发现异常按照前面第 4.3 节的方式隔离环境并重新生成密钥。即使没有发现异常也建议在后续开发中不再使用这种先加载后查杀的流程。6. 生产环境模型安全最佳实践一次事件带不来安全感能够长期执行的安全基线才能降低风险。以下实践可以直接落地到日常开发流程。6.1 优先使用 safetensors 格式加载模型时优先选择仓库中提供的.safetensors文件。PyTorch 可以很方便地把模型参数转换为 safetensorsimport torch from safetensors.torch import save_file model torch.load(model.pt, map_locationcpu, weights_onlyTrue) state_dict model.state_dict() if hasattr(model, state_dict) else model save_file(state_dict, model.safetensors)转换后使用safetensors加载from safetensors.torch import load_file state_dict load_file(model.safetensors, devicecpu)这样可以避免 pickle 反序列化中最危险的执行路径。需要说明的是weights_onlyTrue并不完全阻断所有 pickle 风险PyTorch 曾面临相关挑战更安全的选择是用safetensors替代。6.2 部署前扫描与审计流程在项目 CI 中增加一个模型安全扫描任务示例脚本如下#!/usr/bin/env bash set -euo pipefail TARGET_DIR${1:-./models} echo scanning $TARGET_DIR find $TARGET_DIR -type f \ \( -name *.pt -o -name *.pkl -o -name *.bin \) \ -exec picklescan scan {} \;在 GitHub Actions 或 GitLab CI 中把这个任务加在模型下载之后、模型加载测试之前。扫描结果如果包含高风险调用CI 直接失败。同时维护一个“已知安全模型清单”记录仓库名、commit hash、文件 sha256避免每次下载都凭直觉判断。6.3 最小权限与网络隔离加载模型的进程不要使用 root 账号也不要在~/.bashrc或环境变量中放置长期有效的 API 密钥。建议使用独立用户运行推理服务。给模型加载容器临时挂载只读目录。禁止推理服务访问公网除非明确需要调用外部 API。每次重要模型更新时轮换访问令牌。这样即使模型文件内部存在恶意代码攻击者能获取的信息也被限制在最小范围。6.4 依赖锁定与仓库版本锁定Hugging Face 仓库用revision参数锁定版本示例model AutoModel.from_pretrained( some-org/some-model, revision2a3f4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a, use_safetensorsTrue )同时项目依赖应锁定在requirements.txt或pyproject.toml中transformers4.45.0 torch2.4.0 safetensors0.4.5 picklescan0.0.22避免使用无条件升级因为框架版本的更新可能改变默认加载行为也可能会意外引入不兼容的脚本解析逻辑。6.5 事件响应与回滚方案即使做了充分预防还是可能出现遗漏。需要准备好回滚方案对生产推理环境做镜像快照保留最近 N 个可用版本。模型文件和代码分开存储模型文件放在独立的对象存储或模型仓库中代码发布时不影响权重。如果发现加载的模型包含恶意代码立即切断相关服务的网络并从快照恢复。所有访问密钥都支持紧急撤销不要手动修改配置里的明文 key。响应流程可以简化为先断网再保护现场随后撤销凭据最后恢复服务和复盘。7. 读事件报告的正确姿势与后续行动回到“OpenAI 发布 Hugging Face 事件技术报告”这个主题。真正有价值的部分不是记住某个事件的具体时间点和影响数量而是把报告当成一次安全能力的模拟演练。每看到一类攻击载体就追问我本地有没有对应类型的文件每看到一个缓解措施就对照我的下载流程里是否包含这一步。建议读者完成以下三个动作第一用本文第 3 节的最小流程在自己常用的模型下载环境里执行一次全量扫描把扫描脚本保存到项目仓库中。第二列出当前项目依赖的模型仓库锁定 commit hash记录文件 sha256建立模型来源清单。第三把“模型文件是否使用 safetensors”加入代码评审规则把picklescan加入 CI pipeline。模型供应链安全不是一次性工作。Hugging Face 上的仓库每天都在更新新的攻击手法也在不断出现。能够在一份技术报告发布后快速提取自查清单并持续维护自己的安全基线才是这次事件带给开发者最实在的收获。