CVE-2026-34204: Replication Metadata Injection
Status: Released
First containing release: RELEASE.2026-04-17T00-00-00Z
Fix commit: 56fa63b
GitHub issue: pgsty/silo#24
Before 56fa63b, ordinary PutObject and CopyObject requests could smuggle client-supplied X-Minio-Replication-* headers into internal X-Minio-Internal-* SSE and replication metadata. That mismatch between the stored metadata and the actual write path could leave the resulting object unreadable.
The fix stops accepting replication-only metadata in the default extraction path, restores those fields solely inside flows explicitly authorized for ReplicateObjectAction (s3:ReplicateObject), and sanitizes CopyObject request headers before any downstream header consumer runs.
Threat model
Exploiting this flaw required only ordinary object-write permission (s3:PutObject), not internode or replication credentials. Although the input arrived in client-controlled X-Minio-Replication-* HTTP headers, metadata extraction converted those headers into internal replication and SSE state without checking whether the caller was authorized to replicate objects.
When a subsequent GetObject or HeadObject request read the object, the server interpreted the stored object according to that forged internal state and failed to read or decrypt it. Because any authenticated writer could trigger this integrity and availability failure, every server accepting untrusted S3 writes was affected.
The underlying flaw was that client-controlled headers acquired privileged internal semantics before passing a replication authorization check.
Why we chose surgical stripping over rejecting the whole request
The most obvious fix would have been to reject any non-replication request carrying an X-Minio-Replication-* header. However, returning 400 or 403 whenever an extraneous header appears would turn previously ignored headers into hard client failures.
Instead, we designed a strip-by-default, restore-after-auth model:
- the default metadata extraction helper in
cmd/handler-utils.gonever accepts replication-only metadata; - ordinary
PutObjectandCopyObjectrequests strip those fields before building object metadata; - only code paths that have passed
ReplicateObjectActionauthorization explicitly restore replication-only metadata; - replica-status writes require the same
ReplicateObjectActionauthorization; - legitimate multipart and Snowball replication flows explicitly restore the SSE metadata they require after authorization.
This design closes the injection path while keeping wire behavior unchanged for ordinary clients that happen to pass unrecognized headers.
Why CopyObject had to sanitize headers before cleanMetadata
In CopyObject, request headers are not used solely to construct the final destination metadata map. Earlier stages in the handler inspect request headers for precondition evaluation and SSE-C source handling.
If untrusted X-Minio-Replication-* headers were stripped only inside cleanMetadata right before writing the destination object, those earlier consumers would already have acted on the forged headers. Moving header sanitization to the very beginning of CopyObject—before precondition and SSE-C helpers run—enforces a single invariant across the entire handler: untrusted replication headers never enter internal request semantics.
Implementation and verification
Commit 56fa63b updated cmd/handler-utils.go, object handlers, and multipart handlers, backed by regression tests across multiple layers:
- helper-level unit tests for trusted versus untrusted metadata extraction;
- handler-level tests for malicious
PutObjectandCopyObjectrequests carryingX-Minio-Replication-*headers; CopyObjectearly-sanitization tests verifying that precondition and SSE-C paths do not observe stripped headers;- end-to-end verification confirming that forged replication headers no longer produce unreadable objects, while authorized replication, multipart, and Snowball replication flows continue to succeed.
Trade-offs and subsequent hardening
- Default stripping: Internal replication headers sent by ordinary S3 clients are silently stripped rather than honored.
- Fail-safe default for new endpoints: Because replication-only metadata must be explicitly restored inside an authorized branch, forgetting to call the restore helper in a future replication path causes a functional test failure rather than silently reopening an untrusted injection path.
- Follow-up receiver-wide hardening:
- SN-2026-008 (
RELEASE.2026-09-03T13-18-01Z) and the CORS and replication trust design record generalized this pattern into a single post-authentication trust boundary across all internal replication headers (X-Minio-Source-Etag,X-Minio-Source-Mtime,X-Minio-Source-Replication-Request, replication SSE key headers, andX-Amz-Bucket-Replication-Status), covering SSE-C reads, source timestamps, multipart completion, object lock, deletes, and events. - Replica Metadata Normalization (
RELEASE.2026-09-16T00-00-00Z, PR #194) subsequently ensured that even authorized replicationCopyObjectrequests normalize transport-onlyaws-chunkedContent-Encodingvalues and strip GHSA-redacted metadata keys rather than re-injecting raw request headers into stored objects.
- SN-2026-008 (