---
title: 如何在加载模型前验证其签名
dek: 在任何代码加载权重之前，用这份五步核查表确认模型文件正是预期发布者签署的那些文件，并记录这次检查覆盖了哪些文件。
needs: 模型文件、签名包和预期签名者的身份
disclosure: 调研和起草本指南的程序基于 Anthropic 的 Claude 模型构建。本指南引用了一篇论文，作者为 Anthropic 研究人员及其他机构的合著者。
---

借助模型签名，你可以在任何代码加载权重之前，核查磁盘上的文件是否正是某个已知发布者签署的文件。[^1]这项检查的可靠程度取决于你拿来比对的签名者身份，而且它只覆盖已签名的文件。因此，下面每一步都给出一条命令，并说明它支持什么决定。本指南使用的工具是 `model_signing`，它是 model-signing 软件包的命令行工具，这个软件包来自 Sigstore 的 model-transparency 项目。[^1, 2]截至 2026 年 10 月 20 日，Python 软件包索引（PyPI）上的当前版本是 1.1.1 版。[^2]我们[关于 1.0 版发布的简报](/articles/sigstore-model-signing-stable-release/)介绍了背景。

## 何时使用本指南

当你收到一个已签名的模型，准备用它提供服务、对它做微调，或把它交给另一个系统时，就可以使用本指南。开源安全基金会（OpenSSF）的 AI/ML 工作组曾在 2025 年 4 月宣布 1.0 版发布。该工作组表示，每次使用模型时都应检查签名，包括选定模型用于部署时，以及模型成为另一个模型的输入时。[^8]

## 开始前需要准备什么

- **模型目录**，与你将要加载的目录完全一致，并且由普通文件组成。除非你允许符号链接，否则工具遇到符号链接会报错并停止。[^5]
- **签名包（bundle）**，随模型一同分发，工具默认将其命名为 `model.sig`。[^1, 3]
- **预期的签名者**，从你已经信任的渠道获取，例如发布者的文档。切勿从签名包中复制：签名包记录的是实际签名的一方，而这一方未必是发布者。
  - Sigstore 签名：签名者的 OpenID Connect（OIDC）身份及其签发方（issuer），即身份提供方。身份可以是电子邮件地址；自动签名时，则是工作负载身份，例如某个持续集成（CI）工作流。签发方包括 `https://accounts.google.com`，以及 GitHub Actions 使用的 `https://token.actions.githubusercontent.com`。[^1, 3]
  - 密钥签名：发布者的公钥。
  - 证书签名：发布者的证书链。
- **Python 3.9 或更高版本**，这是 1.1.1 版的运行要求。[^2]

## 第 1 步：安装固定版本

安装一个确切的版本，然后查看工具报告的版本号。

```bash
pip install model-signing==1.1.1
model_signing --version
```

1.1.1 版有几个选项用下划线拼写，例如 `--identity_provider`。[^3]一项尚未发布的改动把这些选项改为连字符写法，同时继续接受下划线写法。[^7]因此，为后续版本编写的命令在 1.1.1 版上可能会失败。请以你所用版本对应标签下的 README 为准。[^1]

- [ ] 在全新的虚拟环境中安装该版本，并记下版本号
- [ ] 不要使用早于 1.0.1 的版本：变更日志称，更早的命令行脚本在验证失败后，仍可能报告通过并以成功状态码退出[^7]

## 第 2 步：对照预期的签名者验证

使用 Sigstore 时，签名者用一张短期证书签名，这张证书对应签名者的 OIDC 身份。签名事件会记录在一份公开、只追加的透明日志中。[^1, 9]身份和签发方是必填参数，如果二者与签名不符，验证就会失败。[^3]

```bash
model_signing verify path/to/model \
  --signature model.sig \
  --identity "$EXPECTED_IDENTITY" \
  --identity_provider "$EXPECTED_ISSUER"
```

使用密钥签名时，请传入发布者的公钥；能把签名与发布者联系起来的只有这把公钥。[^3]

```bash
model_signing verify key path/to/model \
  --signature model.sig \
  --public_key publisher.pub
```

使用证书签名时，你必须自己确认签名者，因为命令会检查证书链，但没有参数可以指定预期的签名者名称。[^3]如果不提供 `--certificate_chain`，命令会改用一组默认的根证书。[^4]有些证书的扩展密钥用法字段不允许代码签名，1.1.1 版却会接受其中一部分，例如为 Web 服务器签发的证书。相应的修复尚未发布。[^7]请传入发布者自己的证书链，并向发布者核实签名证书的 SHA-256 指纹，以此确认签名者；`--log_fingerprints` 会把这个指纹写入日志。[^3]

```bash
model_signing verify certificate path/to/model \
  --signature model.sig \
  --certificate_chain publisher-chain.pem \
  --log_fingerprints
```

- [ ] 预期的签名者来自发布者自己的渠道
- [ ] 运行命令时没有使用 `--ignore-paths`、`--ignore_unsigned_files` 或 `--allow_symlinks`，第 3 步会解释这几个选项
- [ ] 运行命令时没有使用 `--use_staging`；该项目称 Sigstore 的预发布（staging）实例只用于测试签名[^3]

## 第 3 步：读懂结果覆盖了什么

先看输出：命令成功时输出 `Verification succeeded`，失败时输出 `Verification failed with error:` 及失败原因，并以状态码 1 退出。[^3]它会对目录计算哈希。如果某个已签名的文件发生改动或缺失，或者目录中多出了文件，验证就会失败，错误信息会指出是哪些文件。[^4]

对于跳过的路径，验证通过说明不了任何情况；默认跳过的路径包括与 Git 有关的文件。[^1]从 1.1.0 版起，签名还会记录发布者在签名时排除的路径，验证时会自动跳过这些路径。[^7]另有两个选项会在此基础上扩大跳过的范围：`--ignore-paths` 跳过你指定的路径，`--ignore_unsigned_files` 则让多余的文件也能通过。[^3, 7]只有对你的加载器从不读取的文件，才可以使用这两个选项。

不要开启 `--allow_symlinks`。开启后，如果某个符号链接指向目录，1.1.1 版会悄无声息地让这个符号链接之下的所有文件既不签名、也不检查。据变更日志，这一缺陷只在尚未发布的代码中得到修复。[^7]

要查看签名覆盖了哪些内容，可以用 jq 工具解码其中经过签名的声明：[^9]

```bash
jq -r .dsseEnvelope.payload model.sig | base64 -d | jq .
```

在输出中，`predicate.resources` 列出签名覆盖的每个文件，以及它的哈希算法和摘要（即哈希值）。`predicate.serialization` 字段记录哈希设置，包括签名时排除的路径。[^6]如果 `predicateType` 为 `https://model_signing/Digests/v0.1`，说明这是 1.0 版之前的签名。工具可以验证这类签名，但不能创建；用变更日志的话说，它把 0.2 版的实验性行为“连 bug 一起”照搬了过来。[^6, 7]

- [ ] 验证失败后，每个多出、缺失或改动的文件都已查明原因，之后才加载任何内容
- [ ] 加载器读取的每个文件都出现在 `predicate.resources` 中，且没有一个位于跳过的路径中
- [ ] 谓词类型为 `https://model_signing/signature/v1.0`，或者已请发布者重新签名

## 第 4 步：检查透明日志条目

这一步只适用于 Sigstore 签名。这类签名记录在 Rekor 中，Rekor 是 Sigstore 的只追加透明日志。[^1, 11]签名包中有一份收录证明，表明该条目在日志中；verify 命令已经检查过这份收录证明。[^1, 9]签名包还包含该条目的日志索引和收录时间（integrated time），即该条目写入日志的时间。签名包中另有一个经过签名的时间戳，表明签名发生在短期证书的有效期内。因此，证书过期后，验证仍能成功。[^9]

```bash
jq '.verificationMaterial.tlogEntries[] | {logIndex, integratedTime}' model.sig
rekor-cli get --rekor_server https://rekor.sigstore.dev --log-index <log-index>
```

第二条命令用 Rekor 命令行客户端获取该条目。[^10]它查询的服务器托管着 Rekor 的原始日志，这仍是 Sigstore 公共实例的默认日志。各生态系统可以选择改用第 2 版日志，这些日志位于其他地址。[^11, 12]

- [ ] 收录时间与发布者给出的发布日期吻合
- [ ] 如果你的策略要求在线检查，已按索引从日志中取回该条目
- [ ] 你知道发布者是否在日志中监控自己的签名身份，该项目建议签名者这样做[^1]

## 第 5 步：记录结果

保留足够的信息，让审计人员能够重复这项检查。

- [ ] 签名包已与模型、所用命令、命令输出和工具版本一起保存
- [ ] 已记下预期的签名者，以及你从何处获得这一信息
- [ ] 已保存日志索引，以及经过签名的声明中的摘要
- [ ] 每次复制或替换文件后，都重新检查一遍

## 把摘要写入 AI 物料清单

2026 年 5 月，七国集团（G7）成员国的网络安全机构发布了面向 AI 的软件物料清单（SBOM）最低要素。它们把这种清单描述为 AI 系统组件的结构化记录。对于每个模型，清单应给出权重或模型文件的哈希值以及所用的哈希算法，以便验证该组件的完整性。SBOM 本身应带有可归属于其作者的数字签名。[^13]

经过验证的模型签名能提供这样的哈希值，而且它们出自已知的签名者。`predicate.resources` 中的每个条目都给出一个文件的算法和摘要。声明的主体（subject）则带有一个 SHA-256 摘要，它根据所有这些摘要计算得出。按照该项目的设计，其他元数据文档可以用这个摘要指代该模型。[^6]请把这些值复制到该模型的 SBOM 条目中。如果你收到的 SBOM 对同一文件、同一算法给出了不同的摘要，它描述的就不是发布者签署的那个文件。模型签名把文件与发布者联系起来，SBOM 的签名则把组件清单与其作者联系起来。我们[关于最低要素的简报](/articles/g7-ai-sbom-minimum-elements/)列出了全部要素。

## 示例

:::example 示例（假设情形）：内部工具使用的文本摘要模型
一个团队计划在内部工具中部署一个虚构的开放权重模型 `summarizer-7b`。发布者的模型卡写明，每个版本都由 `release@example.com` 通过 Google 的签发方用 Sigstore 签名，下载内容中也包含 `model.sig`。团队运行了以下命令：

```bash
model_signing verify summarizer-7b \
  --signature model.sig \
  --identity "release@example.com" \
  --identity_provider "https://accounts.google.com"
```

1. 命令运行失败，并指出多了一个文件 `handler.py`，而团队的推理服务框架会导入这个文件。团队删除了这个文件，并询问发布者它为什么会在那里，而没有加上 `--ignore_unsigned_files` 重新运行。
2. 第二次运行输出 `Verification succeeded`。
3. 解码后的声明使用 1.0 版的谓词类型，并显示发布者排除了 `README.md`，而团队的加载器不会读取这个文件。
4. 日志条目的收录时间落在模型卡所写的发布日期当天。
5. 团队按第 5 步记录结果，并把权重文件的摘要复制到该模型的 SBOM 条目中。
:::

:::limits 有效的签名不能说明什么
- 检查通过，说明这些文件自预期身份或密钥的持有者签名以来没有改变。它不能说明权重是安全的、未遭投毒或不含后门。发布者签署一个受损的模型，和签署一个干净的模型同样容易。
- 审查模型未必能发现后门。Goldwasser、Kim、Vaikuntanathan 和 Zamir 展示了恶意的训练方如何在分类器中植入后门：没有后门密钥，任何计算能力有限的观察者都无法检测到它。[^14]
- 继续训练也未必能消除后门。Hubinger 及其同事训练出这样的语言模型：提示中称当前年份是 2023 年时，模型编写安全的代码；称当前年份是 2024 年时，则编写可被利用的代码。他们发现，可以让这类后门在监督微调、强化学习和对抗训练之后依然存在。[^15]
- 这项检查不覆盖已签名文件列表以外的文件、加载模型的代码，也不覆盖由他人提供服务的任何模型。
- 如果发布者的账户、CI 工作流或密钥落入攻击者之手，攻击者的签名同样能通过验证。对于 Sigstore 签名，如果发布者在日志中监控自己的身份，就能发现这样的签名。[^1]
:::
