模型文件、数据集、提示词模板、插件工具和推理镜像都可能成为攻击入口。
AI 系统的供应链风险,不再只是“依赖包有没有漏洞”,而是“模型是否可信、数据是否被污染、推理路径是否被替换”。
如果企业已经把大模型接入审批、客服、风控或代码生成链路,AI 供应链安全应当进入上线门禁,而不是事后审计。
问题背景:一次内部智能体上线引出的供应链风险
一个真实可落地的场景:某金融团队建设内部智能体,用于辅助客服质检与风险工单归因。系统由四类资产组成:
- 大语言模型:来自 Hugging Face 或内部模型仓库
- 推理服务:容器化部署,暴露 OpenAI-compatible API
- 工具插件:访问工单、客户画像、审计日志
- 提示词与策略:决定智能体是否能调用敏感工具
传统 DevSecOps 只覆盖代码仓库、镜像扫描、依赖漏洞扫描。但在 AI 系统里,攻击者可以不改一行代码,只替换一个模型权重、投毒一个微调数据集、上传一个恶意 tokenizer,或者修改 Agent 工具描述,让模型错误调用高权限接口。
核心结论:AI 供应链安全的目标不是“扫描更多东西”,而是把模型、数据、提示词、工具和运行时全部纳入可追溯、可验证、可阻断的交付链。
机制上,需要为每个 AI 资产建立身份、来源、哈希摘要、签名、使用范围和审批状态。例证是:同一个 model.bin 如果没有摘要校验,测试环境下载的是官方模型,生产环境被替换成带后门的同名文件,运行时几乎无法通过日志发现。
核心概念:AI 供应链到底包含什么
AI 供应链安全(AI Supply Chain Security)可以拆成五类对象。
| 对象 | 风险 | 治理方式 |
|---|---|---|
| 代码依赖 | 恶意包、漏洞依赖、构建脚本注入 | SBOM、锁版本、依赖审计 |
| 模型制品 | 权重替换、后门模型、反序列化执行 | 摘要校验、签名、格式约束 |
| 数据集 | 数据投毒、隐私混入、版权风险 | 数据血缘、抽样审计、访问控制 |
| 提示词与 Agent 工具 | 越权调用、提示词注入、工具描述投毒 | 策略审批、最小权限、调用审计 |
| 推理运行时 | 镜像污染、环境变量泄露、动态下载 | 镜像签名、出站限制、运行时策略 |
这里有三个容易混淆的概念。
**SBOM(Software Bill of Materials,软件物料清单)**描述软件包含哪些组件。主流格式包括 SPDX 2.3 和 CycloneDX 1.6。它适合描述代码依赖和容器镜像,但对模型权重、训练数据、提示词版本的表达能力有限。
ML-BOM 或 AI-BOM可以理解为面向 AI 资产扩展的物料清单,记录模型来源、数据来源、训练参数、评测结果和授权信息。它还没有像 SBOM 那样完全标准化,因此工程上常用“SBOM + 自定义元数据”先落地。
**SLSA(Supply-chain Levels for Software Artifacts)**关注构建链路可信度,SLSA v1.0 定义了来源证明、构建隔离、不可篡改出处等要求。AI 场景可以复用它的思想:模型不是“下载完就能用”,而应有 provenance,也就是来源证明。
**边界条件很重要:供应链安全不能证明模型“绝对安全”,只能证明它来自哪里、是否被篡改、是否符合上线策略。**模型是否会产生有害回答,还需要红队评测、内容安全和运行时防护配合。
关键机制:从“可信来源”到“上线阻断”
一个工程可用的 AI 供应链治理链路通常分四步。
资产登记:先知道自己用了什么
结论:没有资产清单,后续所有安全控制都会变成抽样检查。
机制是为每个模型、数据集、提示词、工具插件生成唯一标识,并记录版本、摘要、来源和用途。例证:同一个 bge-reranker 模型可能同时用于搜索排序和审批推荐,前者出问题影响体验,后者出问题可能影响合规判断,风险等级不同。
建议最小字段如下:
asset_id: model.customer-risk-reranker
type: model
format: safetensors
source: internal-registry.example.com/risk/reranker
sha256: "9f2a...示例"
license: internal
owner: risk-ai-team
allowed_runtime:
- inference-risk-prod
approved: true
safetensors 是 Hugging Face 推出的安全张量序列化格式,设计目标之一是避免 pickle 反序列化带来的任意代码执行风险。它不能解决后门模型问题,但能降低加载阶段的执行风险。
签名与来源证明:确认没被替换
结论:摘要只能证明“内容一致”,签名才能证明“谁发布的”。
机制是构建完成后对模型制品、容器镜像、提示词包生成摘要,并使用签名工具绑定发布者身份。常见方案是 Sigstore Cosign。Cosign v2.x 支持对 OCI 镜像和通用制品签名,适合接入容器仓库和模型仓库。
例证:如果生产集群只校验文件名 risk-model-latest,攻击者替换标签即可上线恶意模型;如果校验 digest 和签名,替换动作会在准入阶段失败。
策略准入:不合规资产不能进入运行时
结论:安全规则要前移到部署入口,而不是依赖上线后人工排查。
机制是把“允许哪些模型、哪些工具、哪些镜像运行”写成策略,在 CI/CD 或 Kubernetes Admission Controller 阶段拦截。策略可以包含:
- 模型必须来自内部 registry
- 权重格式必须是
safetensors - 镜像必须有签名
- SBOM 必须存在
- 高风险工具必须走人工审批
- 推理容器禁止启动后访问公网下载模型
运行时审计:AI 风险必须看调用链
结论:AI 供应链安全不能停在构建阶段,因为 Agent 会在运行时组合工具。
机制是记录模型版本、提示词版本、工具调用、输入输出摘要和授权上下文。例证:一次错误转账建议可能不是模型本身被污染,而是工具描述被改成“余额查询接口可执行转账”,导致 Agent 误判工具能力。
一个 Rust + Axum 的准入服务示例
下面示例使用 Rust 1.85 与 Axum 0.8,模拟一个模型上线准入接口。真实生产中签名校验应接 Cosign、Rekor 或企业 KMS,这里重点展示“摘要 + 策略”的关键路径。
use axum::{extract::Json, routing::post, Router};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::net::SocketAddr;
#[derive(Deserialize)]
struct ModelAdmissionRequest {
asset_id: String,
source: String,
format: String,
sha256: String,
approved: bool,
bytes_base64: String,
}
#[derive(Serialize)]
struct AdmissionResponse {
allowed: bool,
reason: String,
}
async fn admit_model(Json(req): Json<ModelAdmissionRequest>) -> Json<AdmissionResponse> {
// 1. 策略:只允许内部仓库
if !req.source.starts_with("internal-registry.example.com/") {
return Json(deny("model source is not trusted"));
}
// 2. 策略:优先要求 safetensors,避免 pickle 类反序列化风险
if req.format != "safetensors" {
return Json(deny("model format is not allowed"));
}
// 3. 策略:必须经过审批
if !req.approved {
return Json(deny("model is not approved"));
}
// 4. 完整性:校验提交内容与登记摘要是否一致
let bytes = match base64::decode(req.bytes_base64) {
Ok(v) => v,
Err(_) => return Json(deny("invalid model payload")),
};
let actual = format!("{:x}", Sha256::digest(&bytes));
if actual != req.sha256 {
return Json(deny("sha256 mismatch"));
}
Json(AdmissionResponse {
allowed: true,
reason: format!("asset {} admitted", req.asset_id),
})
}
fn deny(reason: &str) -> AdmissionResponse {
AdmissionResponse {
allowed: false,
reason: reason.to_string(),
}
}
#[tokio::main]
async fn main() {
let app = Router::new().route("/admit/model", post(admit_model));
let addr = SocketAddr::from(([0, 0, 0, 0], 8080));
axum::serve(tokio::net::TcpListener::bind(addr).await.unwrap(), app)
.await
.unwrap();
}
这个服务不是完整安全系统,但它体现了供应链准入的核心思想:上线不是“把文件放进去”,而是“证明它符合策略”。
工程实践与选型
| 方案 | 适用场景 | 优点 | 不适用条件 |
|---|---|---|---|
| 仅依赖漏洞扫描 | 普通后端服务 | 接入快,覆盖 CVE | 无法识别模型替换、数据投毒 |
| SBOM + 镜像签名 | 容器化推理服务 | 适合现有 DevSecOps | 对模型和提示词表达不足 |
| AI 资产登记 + 策略准入 | 企业内部 AI 平台 | 能覆盖模型、数据、工具 | 需要平台化治理能力 |
| 零信任运行时 + 出站限制 | 高风险生产系统 | 能阻断动态下载与横向移动 | 对研发便利性有影响 |
明确结论:
如果只是实验环境,先做依赖锁定、模型摘要校验和私有仓库存储。
机制是减少动态下载和来源不明资产。例证:禁止生产容器启动时从公网拉取模型,可以直接消除大量不可追溯风险。
如果是生产 AI 应用,至少要做到 SBOM、镜像签名、模型登记、推理出站限制。
机制是把代码、镜像、模型和运行时出口同时纳入控制面。例证:客服质检模型即使不接触资金,也可能接触客户隐私,不能只按“非交易系统”降级处理。
如果是 Agent 系统,工具供应链要和模型供应链同级治理。
机制是 Agent 的能力来自工具组合,恶意工具描述可能诱导模型调用错误接口。例证:一个被篡改的“查询订单”插件,如果实际具备“取消订单”能力,模型安全评测再好也无法兜底。
常见误区与踩坑
最常见的坑是:把模型仓库当成 Maven 仓库使用,直接用标签引用模型。
触发条件通常是部署配置写成:
MODEL_NAME: risk-reranker:latest
DOWNLOAD_ON_START: "true"
现象是测试环境、预发环境、生产环境虽然配置相同,但实际加载的模型内容不同;一旦外部仓库模型被删除、替换或访问异常,服务行为会漂移,回滚也无法精确复现。
规避方式:
- 禁止生产使用
latest - 使用 digest 或 sha256 固定模型
- 模型进入内部仓库后再上线
- 推理容器启动后禁止公网下载
- 推理日志记录模型 digest,而不只记录模型名
另一个容易忽略的点是 tokenizer。模型权重没有变化,但 tokenizer 被替换后,输入切分不同,安全规则和评测结果都可能失效。因此 tokenizer、配置文件、special tokens 也应作为模型制品的一部分签名和登记。
知识地图与学习路径
建议按下面顺序学习和落地。
第一步,补齐软件供应链基础:
- SBOM:理解 SPDX 2.3、CycloneDX 1.6 能表达什么
- 签名:理解 Sigstore Cosign 的镜像签名与验证模型
- 来源证明:理解 SLSA v1.0 的 provenance 思路
第二步,理解 AI 特有资产:
- 模型格式:了解 pickle、ONNX、safetensors 的安全差异
- 数据血缘:记录数据集来源、清洗规则、采样审计
- Agent 工具:把工具 schema、权限、描述文本纳入版本管理
第三步,做一个最小闭环:
- 建一个内部模型 registry
- 上传模型时生成 sha256
- 记录模型元数据
- 部署前调用准入服务
- 推理服务日志记录模型 digest 和提示词版本
- 禁止生产运行时动态下载外部模型
第四步,再接入平台能力:
- Kubernetes Admission Controller
- OPA 或 Kyverno 策略
- Cosign 签名校验
- CI/CD 中的 SBOM 生成
- 推理网关中的调用审计
AI 安全不是单点工具问题,而是供应链、权限、运行时和审计共同构成的工程体系。
模型、数据、提示词和工具都应被当作可验证资产管理,生产环境尤其要避免动态下载和不可追溯标签。
你所在团队当前最难纳入治理的是模型、数据集,还是 Agent 工具链?