C2PA排除范围漏洞:签名验证为何必须检查覆盖范围

C2PA 排除范围缺陷揭示签名有效但零覆盖的风险,SAML 与 JWT 同受波及。
文章剖析了 C2PA 规范中排除范围(exclusion range)缺陷的机理:验证器只校验密码学签名是否匹配,却不检查签名实际覆盖的字节范围,攻击者可将整个文件声明为排除区间,使签名数学上成立但保护内容为零。这一缺陷模式并非 C2PA 独有,SAML 的 XML 签名引用范围与 JWT 的 JWS 编码边界同样存在“范围声明被信任而非被验证”的问题。根因在于协议设计低估了签名覆盖范围这一元数据的语义重要性,验证 API 普遍缺少显式的覆盖检查。文章最后给出防御检查清单,包括显式解析范围声明、断言非空覆盖、统一验证与业务解析路径、拒绝隐式默认回退,以及将空范围、全排除、重叠排除等恶意构造纳入回归测试。
签名验证的常见误区是只问“签名有效吗”,却不问“签名到底覆盖了什么”。C2PA 的排除范围(exclusion range)缺陷让这个问题暴露在聚光灯下:攻击者可以构造一份签名仍然通过验证、但实际上没有保护任何字节的文档。更麻烦的是,同样的思路在 SAML 和 JWT 的实现里也能找到影子。以下从漏洞机理、跨协议共性和防御检查清单三个层面展开。

漏洞核心:签名有效不等于内容被覆盖
C2PA 规范允许在签名计算中声明“排除范围”,用于跳过某些不应被签名的字节区间(例如占位符或外部引用)。问题出在验证端:当验证器只校验密码学签名是否匹配,而没有严格检查排除范围是否覆盖了实际内容时,就会出现一种荒谬状态——签名数学上成立,但其覆盖的字节数为零。换句话说,攻击者把整个文件声明为“被排除”,签名依然验证通过,内容却完全处于未保护状态。
这类缺陷的危险性在于它绕过了信任链的语义层。传统签名验证输出的是二元的“通过/失败”,而安全真正需要的是“通过,并且覆盖了预期的载荷”。当验证器缺少第二步判断,签名机制就从完整性保护退化为一张可被任意利用的空白支票。
C2PA(Coalition for Content Provenance and Authenticity) 是一套用于数字内容溯源与真实性的开放技术标准,由 Adobe、Microsoft、BBC 等机构联合推动。它通过在文件中嵌入签名清单(manifest)来记录内容的来源、编辑历史和签发者,常用于区分真实拍摄与 AI 生成或篡改的媒体。C2PA 清单本身是一个 CBOR 结构,包含断言(assertions)、凭证(credentials)和签名块。其中“排除范围”(exclusion range)是清单中的一个字段,用于告知验证器在计算签名哈希时应跳过哪些字节区间——典型场景包括需要后续填充的占位符、外部引用或签名块自身。由于签名是对“文件字节减去排除区间”后的结果做哈希,一旦排除区间被恶意扩大至覆盖全部内容,哈希输入就退化为空,签名仍能通过数学校验,但实际未保护任何载荷。理解这一数据结构是理解该漏洞的前提。
为什么 SAML 和 JWT 也躲不开
据安全研究者的分析,相同的缺陷模式在 SAML 断言和 JWT 令牌中同样存在。SAML 的 XML 签名支持通过 XPath 或 ID 引用指定签名覆盖的元素范围;JWT 的 JWS 则依赖 base64url 编码的载荷分段。两者都有一个共同特征:签名运算的对象是一段“选择出来的字节”,而验证器往往默认选择逻辑是正确的。
一旦攻击者能够影响范围声明(例如注入额外的排除节点、篡改编码边界),就可能让签名覆盖一个空片段或无关片段,而把真正的权限声明留在保护之外。多源分析一致认为,这不是某个库的实现疏忽,而是“签名覆盖范围”这一元数据在协议设计中被低估的后果。不同来源的表述略有差异:一部分研究者强调解析器对范围语义的解释不一致,另一部分则更关注验证 API 缺少显式的覆盖检查返回值。两种视角指向同一个结论——范围必须被验证,而不能被信任。
SAML(Security Assertion Markup Language) 是一种基于 XML 的单点登录与身份联合标准,广泛用于企业 SSO。SAML 断言由身份提供方签名,服务提供方验证签名后信任其中的属性声明。XML 签名(XMLDSig)支持通过 Reference 元素的 URI 属性指定签名覆盖的文档片段,可使用 ID 引用、XPath 表达式或 XPointer 来圈定范围。这种灵活性正是风险来源:如果验证器对 Reference URI 的解析与业务代码对断言的解析不一致,攻击者就能让签名覆盖一个无害片段,而把真正的权限声明置于保护之外,这类攻击被称为 XML 签名包装(XML Signature Wrapping)。JWT(JSON Web Token) 则由 JWS 规范定义,令牌分为头部、载荷、签名三段 base64url 编码,签名覆盖的是“头部.载荷”的 ASCII 字节。JWT 本身没有显式的排除范围概念,但解析器对 base64url 边界、尾部空白或编码变体的不一致处理,同样可能造成签名覆盖与实际使用内容之间的偏差。两者的共性在于:签名保护的是“选择出来的字节”,而选择逻辑的语义正确性往往未被验证。
防御检查清单:验证签名覆盖了哪些字节
把“覆盖范围”提到验证流程的一等公民位置,是目前最务实的修复方向。以下检查项可作为实现参考:
- 显式解析范围声明:在验证前完整解析排除范围或引用范围,拒绝空范围、重叠范围和越界范围。
- 断言非空覆盖:签名通过后,额外校验被覆盖字节数大于零,且包含所有关键字段(如 C2PA 的断言、SAML 的 Subject/Attribute、JWT 的 claims)。
- 统一解析路径:签名验证与业务读取必须使用同一套解析结果,避免“验证时看 A、使用时看 B”的解析差异攻击。
- 拒绝隐式默认:不允许缺失范围声明时静默回退到“全量签名”,也不允许异常范围被宽容处理。
- 回归测试覆盖恶意范围:将空范围、全排除、重叠排除等构造样本纳入测试集,验证器应稳定失败而非通过。
这些检查的价值不止于修补单个 CVE。它们把签名验证从“密码学正确”推进到“语义正确”,而这正是内容凭证、身份断言和 API 令牌共同依赖的安全基础。对使用 C2PA 做内容溯源、或依赖 SAML/JWT 做身份授权的团队来说,现在就该检查自己的验证路径是否真的问过一句:这个签名,覆盖了它声称保护的东西吗?
相关推荐

3分钟看懂DeepSeek:从量化私募到开源AI领跑者的崛起之路
3分钟看懂DeepSeek发展史:从幻方量化的算力积累,到V2的MLA推理降本、V3的557.6万美元训练账单、R1开源引爆全球,再到V4与70亿美元A轮融资,梳理深度求索的关键节点与技术路线。

本地文学创作模型PK:Qwen3.8 27B微调版胜出,DeepSeek v4.1表现平淡
B站UP主横向测评七款本地文学创作模型:海明威、HumanLike Chat基于Qwen3.8 27B微调表现最佳,DeepSeek v4.1叙事平淡,东方朔角色混乱垫底。附三道测试题评分与使用建议。

梁文锋的三次豪赌:DeepSeek为何将82%毛利开源?
DeepSeek为何把82%毛利的推理框架DeepInfer开源,又启动千亿融资?从技术路线、国产芯片到组织纯粹性,解析梁文锋的三次豪赌与开源战略背后的商业与理想逻辑。