CVE-2026-34204:复制元数据注入

普通 PUT/COPY 可以伪造内部复制状态;修复让 replication-only metadata 只在授权复制路径中恢复。

状态: 已发布
首个包含版本: RELEASE.2026-04-17T00-00-00Z
GitHub Issue: pgsty/minio#24

普通 PUTCOPY 请求可以把 X-Minio-Replication-* header 伪装进内部 X-Minio-Internal-* SSE metadata,写出 replication state 与真实授权路径不一致、甚至无法读取的对象。最终修复不再默认接受 replication-only metadata,只在通过 ReplicateObjectAction 的可信复制流程中恢复,并在 CopyObject 的所有 header 消费者之前统一清洗。

威胁模型

攻击者只需要普通对象写权限,不需要 internode credential。输入完全来自客户端可控的 X-Minio-Replication-* header;metadata extraction 却会把它们转换成内部复制或 SSE 状态。

后续读路径按照错误的内部状态解释对象,可能导致对象不可读,形成完整性与可用性破坏。几乎所有接受不受信写请求的生产 server 都应该视为受影响。

问题的根本不是 header 名字本身,而是不可信来源的数据在没有经过复制授权的情况下获得了内部语义

全请求拒绝,还是精确清洗

看到 replication header 就拒绝普通请求,是最直观的修复。但这会把客户端过去可以携带的多余 header,从“被忽略”改变成 hard failure。最终选择了更精确的模型:

  • 默认 extraction path 不接受 replication-only metadata;
  • ordinary PUTCOPY 先剥离这些字段;
  • 只有通过 ReplicateObjectAction 授权后才恢复;
  • replica status 写入使用同一可信条件;
  • multipart 与 Snowball 的合法 replication flow 显式恢复所需的 SSE metadata。

这让兼容性变化停留在内部语义,而不是扩大到所有携带多余 header 的客户端。

为什么 CopyObject 必须提前清洗

CopyObject 的 header 不只用于最终 metadata map,还会提前参与 precondition 与 SSE-C source 处理。如果只在写入对象前删除,早期消费者已经被污染。

最终清洗发生在这些消费者之前,把“不可信 replication header 不进入内部语义”变成单一不变量,而不是依赖每个后续函数记得再检查一次。

实现与验证

改动覆盖 handler-utils、object handler 与 multipart handler,并加入了几层测试:

  • helper 层 trusted/untrusted metadata extraction;
  • handler 层 malicious PUTCOPY
  • CopyObject header sanitization;
  • vulnerable parent 与 patched tree 的红绿对照;
  • live server before/after,确认恶意 header 不再让对象不可读;
  • legitimate replication、multipart 与 Snowball flow 保持可用。

公开发布 lineage 的修复提交为 fcb8f24。这篇文章保留历史验证边界,本次博客整理没有重新启动 live server。

代价与残余风险

  • 普通客户端夹带的内部复制 header 现在会被忽略或清洗;
  • replication-only metadata 必须在授权分支显式恢复;
  • 未来新增复制入口如果忘记恢复,会表现为功能回归,而不是重新放开不受信写入;
  • 本次审计聚焦 replication header,不代表所有 X-Minio-Internal-* 字段都完成了同样的 trust audit。

这个事件留下的审查问题很简单:一个字段看起来像“内部字段”并不能证明它可信,必须继续追问它来自哪里,以及哪一个授权决定允许它获得内部含义。

最后修改 August 2, 2026: init commit (8338d5b)