Back to Blog

大模型防篡改指南:基于 Pirate Face 构建 LLM 完整性校验

2026/9/213 min read
大模型防篡改指南:基于 Pirate Face 构建 LLM 完整性校验

大模型防篡改指南:基于 Pirate Face 构建 LLM 完整性校验

大语言模型输出的完整性正成为安全新边界。通过 Pirate Face 协议,我们可以在不依赖中心化信任背书的前提下,对 LLM 生成内容实施密码学验证,确保响应数据从模型部署端到客户端全程未被篡改。核心思路是引入可验证的凭证(Verifiable Credentials),让每一个 AI 回复都携带一份不可伪造的「身份证明」。

为什么 LLM 需要完整性校验

传统的 API 签名方案通常只验证请求来源,却无法证明响应内容本身就是模型原始产出的结果。在实际工程中,我见过多次因为中间代理、CDN 缓存或恶意节点导致的响应静默替换问题。这种风险在金融交易辅助、医疗建议等高敏感场景中是致命的。

大模型防篡改指南:如何基于 Pirate Face 构建 LLM 完整性校验 - 插图 1

Pirate Face 的设计初衷正是为了解决这个信任断层。它不同于简单的 HMAC 签名,而是借鉴了去中心化身份(DID)体系的思路,允许 LLM 提供商像发行数字证书一样,为每次生成任务签发经过数字签名的结构化凭证。当客户端收到响应时,可以直接验证这份凭证的合法性,从而确信看到的内容就是模型真实输出的结果。

核心架构:从 Prompt 到 Verifiable Presentation

在我目前的落地实践中,整个流程分为三个关键阶段:模型推理时的签名生成、传输层的封装、以及客户端的验证逻辑。

1. 模型端签名生成

当 LLM 完成推理后,系统不应直接返回纯文本,而是先生成一个随机的 challenge(挑战值),并将该 challenge 与模型元数据(如模型版本、推理时间戳、输入 hash)一起,由模型的私钥进行签名。这个签名构成了 Verifiable Credential (VC) 的核心 payload。

import json
import hashlib
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec

# 模拟模型推理后的签名流程
def sign_llm_response(model_private_key, response_text, prompt_hash, model_id):
    # 构建基础数据
    data = {
        "model_id": model_id,
        "prompt_hash": prompt_hash,
        "response_content": response_text,
        "timestamp": int(datetime.now().timestamp())
    }
    
    # 生成数据的 SHA-256 hash 作为签名的消息体
    message = hashlib.sha256(json.dumps(data, sort_keys=True).encode()).digest()
    
    # 使用 ECDSA P-256 进行签名(符合 W3C VC 标准推荐算法)
    signature = model_private_key.sign(message, ec.ECDSA(hashes.SHA256()))
    
    return {
        "credential": data,
        "signature": signature.hex(),
        "algorithm": "ES256"
    }

2. 传输与封装

我将签名结果嵌入到 HTTP 响应的 Header 中,遵循 W3C 的 Verifiable Credentials Data Integrity 规范。这样做的好处是不破坏现有的 API 接口契约,前端无需修改数据结构,只需增加一个验证中间件。

在实际部署时,我建议使用标准的 Authorization 或自定义的 X-LLM-Integrity 头部来传递这个凭证。如果响应经过代理服务器,代理应透明透传这些头部,而不应尝试解析或修改其中的签名数据。

3. 客户端验证逻辑

客户端拿到响应后,第一步是提取公钥。公钥通常托管在远端的 DID Document 中,或者通过 Pirate Face 注册表查询。拿到公钥后,验证过程就是一个标准的非对称签名校验。

async function verifyLLMResponse(responseHeader, publicKey) {
  const { credential, signature } = responseHeader;
  
  // 1. 重新计算数据的哈希
  const messageBuffer = new TextEncoder().encode(JSON.stringify(credential));
  const messageHash = await crypto.subtle.digest('SHA-256', messageBuffer);
  
  // 2. 导入公钥
  const pubKey = await crypto.subtle.importKey(
    'raw', 
    publicKey, // Uint8Array 格式的公钥
    { name: 'ECDSA', namedCurve: 'P-256' }, 
    false, 
    ['verify']
  );
  
  // 3. 验证签名
  const sigBuffer = hexToBytes(signature);
  const isValid = await crypto.subtle.verify(
    'ECDSA', 
    pubKey, 
    sigBuffer, 
    messageHash
  );
  
  if (!isValid) {
    throw new Error('LLM Response integrity check failed');
  }
  
  return credential;
}

常见实现陷阱

在实际集成过程中,我发现最大的挑战在于密钥管理。LLM 提供商需要在安全隔离的环境(如 HSM 或 TPM)中管理签名私钥,绝对不能让私钥暴露在常规的模型推理服务容器中,否则一旦容器被攻破,签名体系就形同虚设。

另一个容易被忽视的点是缓存一致性。如果 CDNs 缓存了带签名的响应,而源站的模型版本发生了更新,用户可能会收到旧版本模型的签名但内容已被替换的矛盾情况。我目前的解决方案是强制对带有完整性签名的响应进行 Cache-Control: no-store 处理,或者在签名中强制绑定唯一的 response_id 以确保每次响应都是独立且不可重放的。

下一步:扩展至 Agent 调用链

单次响应的签名只是起点。在多 Agent 协作的场景下,每个 Agent 的输出都需要经过同样的校验链路,形成一条可信的审计链。如果你正在构建企业级的 LLM 应用基础设施,建议尽早将这套验证机制纳入 API 网关的标准配置中,为未来的合规要求做好准备。

相关阅读

原文出处

本文首发于 大模型防篡改指南:基于 Pirate Face 构建 LLM 完整性校验 — https://lyxq.com.cn/en/blog/llm-integrity-pirate-face

转载或引用请注明出处,商业使用请联系作者获得授权。