摘要

  • Digest 问题条目不仅给出算法,还给出 header,因为内容、所选表示和未编码内容属于不同字节范围;字段名是证据本身的一部分。
  • digest-mismatched-values 证明某个应答组件在某个范围内算出了不同结果,不证明谁改变了字节,也不证明应用未提交或请求可以安全重放。

发送端在压缩前计算了 SHA-256。边缘网关在内容编码后验证。源站又按所选表示进行比较。三个组件使用同一算法,三个计算都忠实于各自看到的字节,结果却不相同。

监控平台只保存了两列:algorithm=sha-256 与 mismatch=true。它删除了 header,于是一次范围错位被描述成链路篡改。自动化随后重发请求,试图“修复损坏”。真正丢失的不是数据,而是证据的层次。

HTTPAPI 工作组的 HTTP Problem Types for Digest Fields 草案,把常见 Digest 失败拆成机器可读的问题类型。本文冻结的是 2026 年 6 月 24 日发布、12 月 26 日到期的 revision 06。截至 2026 年 10 月 2 日,Datatracker 显示它已提交 IESG 并进入 RFC Editor 队列,等待首位编辑;负责 Area Director 为 Mike Bishop,IANA 状态显示所需动作已获 RFC Editor 确认。文件目标是 Standards Track 的 Proposed Standard,但尚无 RFC 编号。文档状态不能替代实现与部署证据。

字段名说明“对什么求值”

RFC 9530 定义的 Content-Digest 面向 HTTP 内容,Repr-Digest 面向所选表示。内容编码、解码与中间转换,会使两个观察点拥有不同字节序列。另一个活跃 Internet-Draft 正在定义 Unencoded-Digest;它面向未编码内容,在本次研究时尚不是 RFC。

因此,问题类型数组中的每一项都带有 header。算法回答“如何计算”,字段回答“对哪一层计算”。只保留前者,就像保存温度数字却删除测量位置:数值仍然精确,事实已经改变。

这也解释了为什么简单的“expected/actual”模型不够。对端故意不在 mismatch 响应中给出服务器计算值,以避免形成 oracle。客户端得到的是不相等结论、提供值、算法与字段,而不是完整的双方计算记录。若日志再删除字段,就只剩一个无法定位的红灯。

三种问题类型停在不同边界

digest-unsupported-algorithms 表示验证者不支持相关字段中给出的算法。unsupported_algorithms 项保留算法和字段;响应还可以用相应偏好字段提示服务器支持的选择及优先顺序。

这是一条能力信息。客户端可以据此构造另一种算法的请求,却不能据此断言下一请求会通过授权、业务校验或事务接纳。算法兼容、范围正确、比较成功和应用提交是四次不同判断。

digest-invalid-values 表示该值不可能由声明的算法生成,例如 SHA-512 值的长度根本不成立。条目可包含人类可读的 reason。原样重发往往仍会失败,因为计算或编码本身有错。

它并不覆盖 Structured Fields 的语法解析失败。字段无法解析时,验证器还没有一个可用的算法和值进行形态判断。把语法错误、值形态错误和比较不一致都叫“哈希错”,会把序列化、计算和传输三类事故送进同一修复流程。

digest-mismatched-values 则要求值已经可比较,但服务器为相应字段范围计算出的结果不同。条目带有算法、提供值与字段。草案说请求可能被中间设备无意修改,发送端在临时传输问题下可以考虑原样重试。这里的“可能”和“可以”都不是归因或授权。

响应只能代表作答组件

一条 Problem Details 响应是某个作答方对其验证路径的陈述。RFC 9457 让类型标识问题类别,让扩展成员携带上下文;它不重写 HTTP 状态码的语义。JSON 中的 status 若出现,也只是建议性字段,中间设备修改真实响应状态后,两者可能不一致。

同样,状态 400 并不证明分布式链条里毫无副作用。CDN、API 网关、服务网格、审计服务、队列适配器与源站可能在不同阶段观察、转换或记录请求。草案没有规定所有系统都必须在任何副作用前完成 Digest 验证。

客户端因此不能从 mismatch 推导出三件事:某个中间设备就是责任方、源站没有做过工作、再次发送一定安全。它只知道从自己收到的应答看,一个命名字段的比较没有相等。

扩展信息也不是完备清单。相关数组是可选的,省略没有特殊含义。一个请求还可能带多个 Digest 或偏好字段,从而出现多个相似问题。数组第一项不是根因,顺序也不是规范性优先级。系统若只保存第一条,就可能在下一次请求中才“发现”原本同时存在的第二条。

重放权限来自应用语义

RFC 9110 把安全方法、幂等方法与非幂等操作分开。幂等请求在连接失败后更适合自动重试,因为重复同一意图预期产生同一效果。客户端不应自动重试非幂等请求,除非它知道实际语义幂等,或能判断原请求从未被应用。代理不得自动重试非幂等请求。一次自动重试再次失败后,客户端也不应继续自动重试。

Digest 问题草案示例同时出现 PUT 和 POST。问题类型不会为方法添加幂等性。真正的重放权限可以来自幂等键、事务 ID、条件请求、应用状态查询或明确的未提交回执。缺少这些证据时,正确动作是对账,而不是把 400 当成“未执行”。

即使范围问题看似明确,重发也仍是一项新动作。发送端可以改为对正确的 Content-Digest 范围计算,但它必须沿用稳定操作身份,并确认首个业务请求的状态。修复证据层不能抹掉事务层的不确定性。

安全设计也限制可见性

服务器不返回自己算出的 digest,是为了避免向客户端提供可利用的计算 oracle。更细的问题信息仍可能暴露支持的算法、字段、验证路径或中间设备存在,从而形成实现指纹。回显的 provided_digest 还可能被复制到日志、工单和第三方分析系统。

因此服务器可以基于风险提供更一般的错误,客户端必须把缺少细节保留为未知。观测系统也应考虑只存安全指纹、限制原值访问,并记录哪个组件看到了哪一种响应。透明度不是无限披露,缺省也不是否定证据。

最小证据封套必须保留范围

Heng Lu 的现实分层在这里非常直接。Digest 值是关于特定字节域的符号声明;解析器和验证器是可执行状态;Problem Details 是作答方的观察;重试是下一次行动;应用提交与用户结果属于更后的现实层。低层诊断不能自动取得高层行动权。

最小初始规范可以很小:稳定请求或操作 ID、方法、目标、尝试次数、幂等授权、Digest 字段、算法、提供值的受保护指纹、内容编码与已知转换、验证组件、HTTP 状态、问题类型、响应来源,以及应用回执。字段名绝不能被“优化”掉。

运行代码应主动测试:压缩前后计算、表示转换、多字段、不同数组顺序、扩展省略、不可解析语法、不可能长度、有效 mismatch、应用提交后响应丢失、带与不带幂等键的 POST。正确系统会显示每层已知与未知,而不是把所有值压成一个完整故事。

Digest 的价值在于它能对一组明确字节提出可验证主张。字段身份正是这份主张的边界。一个正确哈希若被搬到错误层,不会变得“差不多正确”;它会成为对错误现实的精确证明。

来源