AI供应链安全的落地治理方案

2 阅读10分钟

模型文件、数据集、提示词模板、插件工具和推理镜像都可能成为攻击入口。
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、权限、描述文本纳入版本管理

第三步,做一个最小闭环:

  1. 建一个内部模型 registry
  2. 上传模型时生成 sha256
  3. 记录模型元数据
  4. 部署前调用准入服务
  5. 推理服务日志记录模型 digest 和提示词版本
  6. 禁止生产运行时动态下载外部模型

第四步,再接入平台能力:

  • Kubernetes Admission Controller
  • OPA 或 Kyverno 策略
  • Cosign 签名校验
  • CI/CD 中的 SBOM 生成
  • 推理网关中的调用审计

AI 安全不是单点工具问题,而是供应链、权限、运行时和审计共同构成的工程体系。
模型、数据、提示词和工具都应被当作可验证资产管理,生产环境尤其要避免动态下载和不可追溯标签。
你所在团队当前最难纳入治理的是模型、数据集,还是 Agent 工具链?