状态:草案(2026-10-07)。 加解密速度、完整性校验,以及将解密后的权重放入内存卷并启动 vLLM 服务器的路径(0.6B 模型)为笔者实测数值。机密计算因缺乏硬件未能直接测试,通过查阅并确认论文与厂商文档的描述进行了引用。未测试大模型及在多个 GPU 上的加载(未实测)。 写作规则:以实测数据为基础,未能测出的标记为「未实测」。
目录
- 1. 先明确问题:我们要保护权重免受什么侵害
- 2. 方法
- 3. 成本:变慢多少,变大多少
- 4. 完整性:检测篡改与偷梁换柱
- 5. 无法防御的内容:运行中的权重是明文
- 6. 密钥放在哪里
- 7. 面试可能会问的问题
- 外部依据
- 未能确认的事项
- 参考资料
- 已确认的版本与日期
1. 先明确问题:我们要保护权重免受什么侵害
如果不明确威胁,「加密模型」这句话就毫无意义。权重泄露的途径有很多,加密只能防住其中的一部分。
| 威胁 | 示例 | 静态加密 | 其他所需内容 |
|---|---|---|---|
| 存储库、备份、镜像仓库中的泄露 | 存储桶权限错误、备份磁带丢失、镜像层泄露 | 防御(若密钥独立存放) | 将密钥存放在与存储不同的地方 |
| 传输中被窃听 | 节点与存储之间 | TLS 进行防御,静态加密属于双重防护 | mTLS |
| 文件篡改与偷梁换柱(供应链) | 替换为恶意权重、改变顺序 | 认证加密(GCM)可检测(在第 4 节中确认) | 签名与来源验证 |
| 运行中的服务器的内存转储 | 节点 root,同一节点的恶意 Pod,调试器 | 无法阻止(解密后的权重存在于内存中) | 访问控制,机密计算 |
| 向服务 API 复制功能 | 通过大量查询收集输出以训练模仿模型 | 无关 | 速率限制,异常检测,合同 |
| 向服务 API 提取参数 | 从暴露对数几率(logits)和对数概率的 API 中恢复最后一层的实际参数(Carlini 等人,ICML 2024) | 无关 | 限制暴露的字段(禁止组合使用 logprobs 和 logit bias) |
本文重新审视了第一行和第三行(静态加密与完整性)的成本与效果,并总结了为什么第四行依然存在。
2. 方法
| 项目 | 值 |
|---|---|
| 目标 | Qwen3-1.7B 的 safetensors 两个分片中较大的一个(3.44GB) |
| 密码 | AES-256-GCM(cryptography 库),每个 64MiB 块使用 12 字节随机 nonce,16 字节标签 |
| 附加认证数据(AAD) | 块编号(8 字节)。用于检测即使块顺序被篡改的情况 |
| 存储格式 | 每个块:(长度 4 字节,nonce 12 字节,密文,标签 16 字节) |
| 环境 | 容器(CPU 限制 6 核,临时磁盘),计算仅使用 CPU |
| 测量 | 文件加解密吞吐量(包含磁盘读写),内存中的纯计算吞吐量(无磁盘,重复 64MiB 缓冲区) |
3. 成本:变慢多少,变大多少
| 项目 | 值 |
|---|---|
| 读取明文文件(包含页面缓存,接近上限) | 2.15 GB/s |
| 加密,文件到文件 | 0.56 GB/s(1 个线程和 4 个线程相同) |
| 解密,文件到内存 | 1 个线程 0.78,2 个线程 0.92,4 个线程 0.95 GB/s |
| 纯计算,AES-256-GCM 加密 | 1 个线程 1.81,2 个线程 2.10,4 个线程 2.21 GB/s |
| 纯计算,AES-256-GCM 解密 | 1个线程 1.77,2个 2.10,4个 2.20 GB/s |
| 文件大小增加 | 3.441GB 变为 3.441GB(每个 64MiB 块 32 字节,约 0.00005%) |
- 文件大小实际上几乎没有增加。 每个块仅附加 12 字节的 nonce、16 字节的标签和 4 字节的长度。
- 解密速度为读取明文速度的 36~44%(0.78~0.95 比 2.15 GB/s)。不过 纯计算速度为 1.8~2.2 GB/s ,因此在此测试的文件路径中,变慢的原因不是计算,而是 I/O 和 Python 端的处理。我们没有对这种区别进行详细拆解(推测)。
- 增加线程带来的收益很小。 计算速度从 1 个线程到 4 个线程仅增加了 22%(从 1.81 增加到 2.21),而文件路径速度增加了 22%(从 0.78 增加到 0.95)。起初我们推测这是因为线程共享了相同的内存带宽,但在下面的“CPU 增强测试”中,这一推测被证明是错误的。
- 这是一个具备硬件加速(AES 指令集)的 CPU。 无加速时的速度是通过在下面的“CPU 增强测试”中通过环境变量关闭加速来测量的。
冷启动增加的时间
如果直接使用约 0.9 GB/s 的解密吞吐量,按权重大小计算的附加时间如下(基于计算,本测试的文件路径值)。
| 模型大小 | 解密附加时间(0.9 GB/s) | 如果是纯计算速度(2 GB/s) |
|---|---|---|
| 3.4GB (1.7B bf16) | 约 3.8 秒 | 约 1.7 秒 |
| 7GB (12B 4-bit) | 约 7.8 秒 | 约 3.5 秒 |
| 140GB (70B bf16) | 约 156 秒 | 约 70 秒 |
140GB 是外推计算得出的,并未直接测量。由于 70B 模型通常分摊加载到多个 GPU 上,如果按 GPU 并行解密,时间会被分摊(分进程的并行测试见下方“CPU 增强测试”)。与第 14 篇中的服务器准备时间(重启 35~55 秒,其中权重加载占 4~6 秒)相比,解密的几秒钟在小模型中占比较显眼,而在大模型中则会被模型下载和加载时间所掩盖。
4. 完整性:检测篡改和置换
GCM 在加密的同时通过标签检查篡改(NIST SP 800-38D)。我们直接尝试了两种情况。
| 尝试 | 结果 |
|---|---|
| 翻转密文块的 1 个字节 | InvalidTag 拒绝解密 |
| 将第 1 个块放到第 0 个位置(顺序置换) | InvalidTag 拒绝解密(因为块编号已纳入 AAD 中) |
| 正常解密结果的 SHA-256 | 与原文一致 |
- 如果不将块编号放入 AAD 中,则无法检测到顺序置换。 由于每个块在其自身的标签下都是正确的,因此绑定位置的信息必须包含在认证目标中。我们进行了直接验证。当加密四个块并交换其中两个块的位置时,如果没有 AAD,解密 无错误成功(内容与原文不同),而如果将块编号放入 AAD 中,则
InvalidTag。 - 通过截断文件末尾来完全剔除块的攻击未在此测试中涉及。我们进行了直接验证。对于删除了最后一个块的文件,如果 AAD 仅包含块编号,则 3 个块会 无错误地解密,标签无法告知文件已被截断。如果在 AAD 中包含总块数,当篡改了头部的总数时,所有块
InvalidTag,而如果保持头部不变,比较读取数量和总数的步骤(代码中的assert)会捕获截断。也就是说,将总数绑定到 AAD 以及进行数量检查这两者都是必需的。 - nonce 是每个块随机的 12 字节。NIST SP 800-38D 第 8.2.2 节要求随机方法 IV 的随机字段至少为 96 位,第 8.3 节将使用相同密钥进行认证加密调用的总数限制在 2 的 32 次方以下。如果划分为 64MiB 的块,使用同一密钥的此限制约为 256PiB(2 的 32 次方 × 64MiB,由笔者计算)。140GB 模型大约有 2,100 个块,因此使用同一密钥加密此类模型约 200 万次仍在限制范围内,在同一个文件中 nonce 重复的概率通过生日悖论近似计算约为 2 的 -75 次方(笔者计算)。由于限制计算的是加密调用次数,因此在重复使用同一密钥重新加密的密钥轮换流程中,调用次数会累积。解密不计入次数。
- 还必须在 AAD 中包含文件标识符(这是笔者的推断,并非 NIST 的要求)。 本文的 AAD 仅包含块编号和总块数。如果用同一密钥加密多个文件,即使互换不同文件中相同编号的块,也会通过标签验证。必须使用每个文件各自包裹的密钥(DEK)或在 AAD 中包含文件标识符,这一点与第 6 节中关于 DEK 的建议相吻合。我们进行了直接验证。在使用同一密钥创建的两个文件中,当将文件 A 的第 1 个块放入文件 B 的第 1 个位置时,如果 AAD 仅为(块编号,总数),则 解密成功且第 1 个块的内容为文件 A 的内容。如果在 AAD 中添加文件标识符,则
InvalidTag。
密文与明文无法区分
前 1MiB(包含头部)的字节熵为:明文(safetensors,bf16 权重)6.34 比特/字节,密文 8.00 比特/字节。权重文件保留了统计结构,可以通过压缩或模式分析提供线索,但密文则等同于随机数据。由于加密后的文件无法被压缩,因此 必须在加密前进行压缩 。
如果将其插入服务引擎加载路径,会增加多少开销?
我们将解密后的权重放置在内存卷(/dev/shm)中,并尝试在该路径下启动 vLLM 服务器。模型为 Qwen3-0.6B(safetensors 1.50GB),配备一张 8GB 级 GPU,块大小为 64MiB,并将块编号和总块数放入 AAD 的配置。服务器准备时间是从启动服务器进程到收到健康检查响应的时间,解密所花费的时间不包含在此值中。
| 分类 | 测量值 |
|---|---|
| 加密 (1.50GB) | 2.43 秒,0.62 GB/s |
| 解密 3 次(到内存卷) | 2.02、2.10、2.18 秒(0.69~0.75 GB/s)。包含 Python 启动和导入在内的整个调用为 2.3~2.4 秒 |
| 从明文目录准备服务器 | 68.0 秒(首次启动),29.4 秒,28.3 秒 |
| 从解密路径准备服务器 | 49.4 秒(首次启动),28.3 秒,27.3 秒 |
| 篡改(密文反转 1 个字节) | 在解密阶段 InvalidTag 中断。未创建权重文件(仅复制了配置文件),未达到服务器启动阶段 |
解读如下。
- 稳态下的额外开销就是解密时间本身。 从第二次启动开始,服务器准备时间与明文和解密路径处于同一水平(28 秒左右),前面附加了约 2.3 秒的解密时间。在此模型中,冷启动增加了约 8%。
- 首次启动两者都很长。 明文首次启动的 68.0 秒可能是因为编译结果不在缓存中,而解密路径的首次启动 49.4 秒推测是因为模型路径是第一次而重新生成了缓存(未通过日志确认原因)。由于路径或配置每次改变时可能会延长一次,因此该效果可能会大于解密成本。
- 篡改会在服务器启动之前被拒绝。 前提是配置了如果解密阶段失败则不启动服务器(启动脚本检查解密的退出代码)。
- 本测试仅在一个 0.6B 模型和一张 GPU 上进行。未测量 140GB 模型或多个 GPU 时的数值(未实际测量)。
CPU 补充测试:关闭加速、并行化
我们仅使用 CPU 重新测试了上述测试中留下的疑问(一次性 Pod,22 核服务器 CPU 的一个节点, cryptography 50.0.2,64MiB 块,仅使用内存)。由于该节点上同时运行着其他工作负载,相同测试的绝对值波动了约 40%(单个进程的解密速度为 1.20 GB/s,而线程测试中有一个达到了 2.00 GB/s)。因此,仅比例具有参考意义。
| 测试 | 结果 |
|---|---|
| 默认(使用 AES-NI、PCLMULQDQ) | 解密 2.01 GB/s,加密 2.08 GB/s(单线程) |
OPENSSL_ia32cap=~0x200000000000000 关闭 AES-NI |
0.47 GB/s(慢约 4.3 倍) |
| 同时关闭 AES-NI 和 PCLMULQDQ | 0.29 GB/s(慢约 6.9 倍) |
| 使用 1、2、4、8 个线程进行解密(单进程) | 2.00、2.52、2.41、2.44 GB/s |
| 使用 1、2、4、8 个进程进行解密 | 1.20、2.30、4.24、6.07 GB/s(相比 1 个进程为 1.9、3.5、5.1 倍) |
- 如果没有 AES 指令,解密速度会慢 4~7 倍。 对于 140GB 模型,上述外推法(约 70 秒)将在 5 分钟左右(计算得出)。云端的绝大多数通用实例都带有加速,但如果是由于虚拟化配置导致 CPU 标志被隐藏的环境,则需要进行确认(
grep -m1 -o aes /proc/cpuinfo). - 线程在 2 个时就停滞了,但进程随着数量增加而增加。 最初对内存带宽的推测是错误的。相比该节点的内存带宽,2.5 GB/s 是一个非常小的值,当分割为进程时,在 8 个进程时上升到了 6 GB/s。本测试并未剖析在单个 Python 进程内部线程并行受阻的原因(原因未确认,可能是解释器层面的限制)。实际应用中,采用将块分配给各个进程(或每个 GPU 独立的加载进程)进行解密的配置是正确的。
- 局限性:这是单节点上的单一测试,且排除了文件 I/O。未实际测量将模型实际分配并加载到多个 GPU 上的时间(未实际测量)。
5. 无法阻止的事项:运行中的权重是明文
服务器若要使用模型,必须将解密后的权重加载到内存(包括 GPU 内存)中。从那一刻起, 节点的 root 用户或同一节点上被入侵的进程就可以读取内存。 由于普通 GPU 不会对内存进行加密,因此仅靠静态加密无法防范此威胁。
NVIDIA 的技术博客(2023-05-31)说明,H100 GPU 提供了机密计算功能,可与 CPU 的可信执行环境(AMD SEV-SNP、Intel TDX)协同工作。本文未能直接测试该功能(没有数据中心 GPU)。该公司 2023-08-03 的博客这样描述保护方式:CPU TEE 对虚拟机内存进行加密,GPU 通过硬件隔离,CPU 和 GPU 之间的数据通过加密的弹跳缓冲区(bounce buffer)传输。另一方面,它提到 GPU 封装之上的 HBM 被认为对于日常物理攻击工具是安全的,因此不进行加密。因此,将机密计算称为“GPU 内存加密”是不准确的,其防范的对象是主机侧的明文暴露以及主机管理员的访问。此外,同一篇文章描述称,由于 CPU 加密性能的限制,CPU 与 GPU 之间的带宽约为 4GB/秒,而在本文启动时加载权重的路径中,该值可能会决定加载时间(这是厂商的描述,未能直接测量)。如何安全地传递密钥(通过证明、attestation 后公开密钥)也是一个独立的设计问题。
因此,实操层面的防护分层如下:
- 存储侧: 将权重加密存储,密钥存放在 KMS 或密钥管理工具中。存储桶权限错误不会直接导致泄漏。
- 加载侧: 启动时获取密钥并解密。为避免解密后的明文残留在磁盘上,如果使用基于内存的卷(
emptyDir的medium: Memory),则节点磁盘上不会残留明文。由于该卷会占用与模型大小相等的 RAM,因此必须将其纳入节点内存和 Pod 内存限制中进行计算(计算得出,未进行直接测试)。 - 运行侧: 节点访问控制(禁止 SSH、禁止调试 Pod)、Pod 安全配置,必要时使用机密计算节点。
- 输出侧: 速率限制与异常查询检测。即使不窃取权重,也可以通过收集输出构建模仿模型。
6. 密钥存放在何处
- 不能存放在与镜像或权重存储库 相同的地方。 否则,一次泄漏就会导致两者同时暴露。
- 密钥应在 Pod 启动时从外部 KMS 获取(通过 Pod 的身份进行认证),切勿以明文形式长期保留在环境变量中。
- 需要制定更换密钥的流程(重新加密)。以 0.56 GB/s 的速度重新加密 3.4GB 文件大约需要 6 秒(计算得出),但由于大模型耗时较长, 采用将包装文件的密钥(DEK)单独存放并用主密钥对其进行包装的 方式可以降低密钥轮换成本。
7. 面试中可能出现的常见问题
- 对模型权重进行加密可以保护什么? 存储、备份及注册表泄漏与篡改(若使用 GCM)。无法阻止运行时的内存转储。
- 为什么要使用 GCM? 同时实现加密和篡改检测。翻转 1 个字节就会导致解密被拒绝。
- 按块分块加密时需要注意什么? 将块号放入附加认证数据中以防止顺序篡改,将块数放入认证对象中并检查读取的数量以防止截断,同时放入文件标识符以防止混入其他文件的块(这三点均经过直接验证),并确保 nonce 不重复。
- 解密会使冷启动增加多少时间? 在本测试中约为 0.7~0.9 GB/s,因此 7GB 模型大约需要 8~10 秒(外推法),而 0.6B 模型实际增加了约 28 秒的服务器准备时间和约 2.3 秒的解密时间。计算本身达到了 2 GB/s 级别。
- 如何保护运行中的模型? 节点访问控制与机密计算(CPU TEE 与 GPU 隔离、PCIe 段加密。HBM 本身不加密)。本文仅查阅了论文和厂商文档。
外部依据
对于未能直接测量或仅凭测量尚不足够的论断,我们查阅并确认了以下资料。“仅摘要”意为未阅读正文,仅阅读至摘要部分。
| 论断 | 资料 | 确认的内容 |
|---|---|---|
| nonce 与调用次数限制 | NIST SP 800-38D(2007) 第 8.2.2 节、第 8.3 节 | 随机字段不低于 96 位,同一密钥的认证加密调用总数不超过 2 的 32 次方。截至 2026 年正在修订中,但有效文档仍为 2007 年版 |
| 机密计算的保护范围 | NVIDIA 技术博客(2023-08-03) | HBM 不加密,通过 CPU TEE、隔离及加密的弹跳缓冲区进行保护。CPU 与 GPU 之间约 4GB/秒(厂商描述) |
| H100 机密计算的推理开销 | Zhu 等人,arXiv 2409.03992 (Confidential Computing on NVIDIA Hopper GPUs: A Performance Benchmark Study) | 在大多数普通查询中低于 7%,在大模型和长序列中几乎为 0。70B 模型通过 4 位量化加载到单张卡上 |
| 同主题的其他测量 | Chrapek 等人,arXiv 2509.18886(仅摘要) / Ibarra 等人,arXiv 2505.16501(仅摘要) | 吞吐量下降 4~8%(Llama2 7B~70B) / 在频繁更换模型的配置中,非机密侧的吞吐量高出 45~70%。将差异归因于将模型加载到 GPU 时的加解密成本 |
| 通过 API 提取参数 | Carlini 等人,ICML 2024 (arXiv 2403.06634) | 从暴露对数概率和 logits 偏差的 API 中恢复最后一层。应对措施中,限制暴露字段比速率限制是更实际的措施 |
| 通过 API 复制功能 | Tramèr 等人,USENIX Security 2016 (arXiv 1609.02943,仅摘要) | 即使隐藏置信度分数,有时仍会被提取 |
| 推理服务器被攻陷时的窃取 | Rinberg 等人,arXiv 2511.02620(仅摘要) | 将通过正常响应隐藏并窃取权重的威胁进行规范化,并提出了基于验证的检测方法 |
也有未能查阅到的资料。由于无法访问原文,未能查阅 RAND 的模型权重保护报告(2024),因此本文未记录关于泄漏案例频率的数据。
未能确认的事项
- 机密计算的性能成本与实际保护范围。由于缺乏硬件未能进行测量,仅能通过外部依据的范围(推理通常为个位数百分比,视条件最高可达约 20%,在更换模型的配置中差异为 45~70%)进行推测。
- 大模型及多 GPU 时的加载时间。 仅测量了单个 0.6B 模型的数值,未测量并行解密或张量并行配置。
- 使用 GPU 解密的方式,以及将大模型实际分割并加载到多个 GPU 时的并行解密时间(CPU 补充测试仅测量到了进程并行级别)。
- 密钥管理系统(KMS)的调用延迟及发生故障时的行为。
参考资料
- NIST SP 800-38D, GCM 与 GMAC 建议:https://csrc.nist.gov/pubs/sp/800/38/d/final
cryptography库的 AEAD 文档:https://cryptography.io/en/latest/hazmat/primitives/aead/- NVIDIA 技术博客,使用机密计算保护敏感数据与 AI 模型(2023-05-31):https://developer.nvidia.com/blog/protecting-sensitive-data-and-ai-models-with-confidential-computing/
- NVIDIA 技术博客, Confidential Computing on NVIDIA H100 GPUs for Secure and Trustworthy AI(2023-08-03)
- Zhu 等,Confidential Computing on NVIDIA Hopper GPUs: A Performance Benchmark Study:https://arxiv.org/abs/2409.03992
- Carlini 等,Stealing Part of a Production Language Model:https://arxiv.org/abs/2403.06634
- 本系列第 14 篇(服务器准备时间)、第 8 篇(safetensors 格式与模型加载)
确认的版本与日期
截至 2026-10-07。 cryptography 库(AESGCM)、容器内的 Python 3.12、Qwen3-1.7B safetensors(速度测试)与 Qwen3-0.6B(服务器加载路径测试,vLLM 0.31.0)。