跳转到主要内容

CVE-2026-41145:Unsigned-Trailer 查询认证绕过

查询字符串 SigV4 凭据进入 STREAMING-UNSIGNED-PAYLOAD-TRAILER 数据流后未被验签;提交 f444b6f37 将预签名拒绝与 SigV4 验签统一收口至共享的 newUnsignedV4ChunkedReader()。

状态: 已发布
首个包含版本: RELEASE.2026-04-17T00-00-00Z
修复提交: f444b6f37
GitHub Advisory: GHSA-hv4r-mvr4-25vw

在 f444b6f37 之前,查询字符串(query-string)形式的 SigV4 凭据可以进入 STREAMING-UNSIGNED-PAYLOAD-TRAILER 上传流,但旧处理器仅在存在 HTTP Authorization 请求头时才执行签名验证。因此,攻击者只要知道一个有效的 Access Key ID,无需构造正确签名即可完成对象或分片写入。

最终修复将预签名请求拒绝与 SigV4 签名验证直接收口进 newUnsignedV4ChunkedReader()(cmd/streaming-v4-unsigned.go),让所有消费该分块数据流的调用方强制共用同一道认证边界。

提交编号说明

2026-04-16 编写补丁时正式 CVE 编号尚未分配,Git 提交标题临时写为 fake CVE-2026-40027。GHSA-hv4r-mvr4-25vw 发布后,正式编号确定为 CVE-2026-41145。历史提交对象 f444b6f37 保持原样,台账与文档统一使用正式编号。

根因:将签名验证绑定到了凭据传输形态

受影响入口包括 PutObject 与 PutObjectPart(UploadPart)。当请求指定 X-Amz-Content-Sha256: STREAMING-UNSIGNED-PAYLOAD-TRAILER,并将 SigV4 凭据与签名(X-Amz-Credential、X-Amz-Signature 等)放在 URL 查询字符串而非 Authorization 请求头中时,旧代码的两处假设发生了碰撞:

  1. 处理器用“Authorization 请求头是否非空”来决定是否调用 reqSignatureV4Verify,误以为请求头为空就代表纯匿名请求;
  2. 身份提取与 newUnsignedV4ChunkedReader() 仍然照常读取分块请求体,并将该次写入归属到查询参数 X-Amz-Credential 指定的 Access Key 名下。

结果是:携带查询字符串凭据的 unsigned-trailer 请求跳过了签名校验,静默降级为“只要报出有效 Access Key 标识即可写入”。

为什么把守卫下沉到共享 Reader,而非逐个修补 Handler

候选方案 风险 结论
在 PutObject 与 PutObjectPart 中各自修补请求头/查询参数判断 仅闭合当前两个入口;其他或未来新增调用 newUnsignedV4ChunkedReader() 的入口极易再次漏检(正如 CVE-2026-40344 所示) 否决
额外设计兼容预签名查询参数的 STREAMING-UNSIGNED-PAYLOAD-TRAILER 协议 在没有既有 S3 协议支持契约的情况下大幅扩张流式鉴权状态机与测试面 否决
在 newUnsignedV4ChunkedReader() 入口统一拒绝预签名凭据并强制验签 所有消费该数据流的调用方强制经过同一道认证门禁 接受

需要注意,纯匿名 STREAMING-UNSIGNED-PAYLOAD-TRAILER 请求并未被一刀切禁用:当请求未携带任何认证凭据、且目标桶策略(Bucket Policy)显式允许匿名 s3:PutObject 时,它仍按匿名授权路径正常工作。真正被禁止的是“携带了查询凭据,却未对该凭据执行验签”的混合状态。

实现与验证

提交 f444b6f37 在 cmd/streaming-v4-unsigned.go 的 newUnsignedV4ChunkedReader() 入口完成预签名拒绝与 SigV4 签名验证,同时移除了 PutObject 与分片上传处理器中依赖 Authorization 请求头是否存在的条件判断。

cmd 包新增的回归测试覆盖:

  • 拒绝 STREAMING-UNSIGNED-PAYLOAD-TRAILER 模式下伪造查询签名的 PutObject 与 PutObjectPart 请求;
  • 拒绝同时混用请求头与查询参数凭据的请求;
  • 验证标准请求头认证的 unsigned-trailer 上传以及桶策略允许的纯匿名写入继续正常工作。
go test ./cmd -run 'Test.*UnsignedTrailer.*|Test.*StreamingUnsigned.*' -count=1

兼容性与运维边界

  • 明确拒绝预签名 unsigned-trailer:预签名/查询字符串 SigV4 凭据与 STREAMING-UNSIGNED-PAYLOAD-TRAILER 的组合现已明确不受支持,属于有意的不兼容收紧(Breaking Change)。
  • 下沉至 Reader 的结构性收益:当多个处理器共享同一种认证数据流时,将认证门禁放进 newUnsignedV4ChunkedReader() 能够从结构上防止 PutObject、PutObjectPart 或 PutObjectExtract(CVE-2026-40344)再次因外层分支不一致而漏检。