Skip to content

Conversation

@tchaikov
Copy link
Contributor

@tchaikov tchaikov commented Nov 21, 2025

Add defense-in-depth limits to multipart/form-data parsing for POST object
to prevent DoS attacks via oversized uploads:

  • MAX_FORM_FIELD_SIZE: 1 MB per field (matching MinIO)
  • MAX_FORM_FIELDS_SIZE: 20 MB total for all form fields
  • MAX_FORM_PARTS: 1000 maximum parts
  • MAX_POST_OBJECT_FILE_SIZE: 5 GB for file content (matching S3 single PUT limit)

Why buffering instead of streaming:

A pure streaming solution using FileStream was initially attempted, but it
breaks compatibility with s3s-proxy and other consumers that use the AWS SDK.
The AWS SDK requires a known Content-Length to compute SHA-256 checksums for
SigV4 request signing. Since FileStream returns an unknown remaining length,
the SDK throws UnsizedRequestBody errors.

MinIO can accept streaming POST uploads directly because its HTTP server
parses multipart boundaries at the protocol level without needing size upfront.
However, when requests are proxied through s3s-proxy using the AWS SDK, the
SDK's cryptographic signing algorithm requires the body size before transmission.

The buffering approach with size limits provides:

  1. Protection against unbounded memory allocation (DoS mitigation)
  2. Compatibility with AWS SDK-based consumers (s3s-proxy, etc.)
  3. Known content length for downstream request signing

Fixes #370

Copilot AI review requested due to automatic review settings November 21, 2025 02:24
Copilot finished reviewing on behalf of tchaikov November 21, 2025 02:29
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements a critical security fix to prevent unbounded memory allocation DoS attacks in POST object multipart/form-data handling. The fix follows MinIO's defense-in-depth approach by implementing multiple independent size limits at different layers, transforming the file upload mechanism from buffering to true streaming.

Key Changes:

  • Added three safety constants: MAX_FORM_FIELD_SIZE (1 MB), MAX_FORM_FIELDS_SIZE (20 MB), and MAX_FORM_PARTS (1000)
  • Implemented streaming file handling instead of buffering entire files in memory
  • Extended request infrastructure to support both known-length (vec_stream) and unknown-length (file_stream) body streams

Reviewed Changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
crates/s3s/src/http/multipart.rs Implements defense-in-depth protections with buffer size checks, per-field limits, total size tracking, and parts count validation; adds comprehensive unit tests for all limit enforcements
crates/s3s/src/ops/mod.rs Removes unsafe aggregate_unlimited() call that buffered entire files in memory; now passes file streams directly to backends for true streaming
crates/s3s/src/http/request.rs Adds file_stream field to S3Extensions to support unknown-length streaming alongside existing vec_stream for known-length data
crates/s3s/src/ops/generated.rs Updates PutObject multipart deserialization to gracefully handle both vec_stream (with content-length) and file_stream (without content-length)
crates/s3s/src/ops/generated_minio.rs Mirrors the generated.rs changes for MinIO compatibility variant
codegen/src/v1/ops.rs Updates code generation template to produce the dual-path stream handling logic for POST object operations
crates/s3s/src/service.rs Adjusts future size limits by 100 bytes (~3%) to accommodate the new file_stream field; minimal overhead for preventing unbounded memory consumption

@tchaikov tchaikov force-pushed the protect-post-unbounded-alloc branch from dd7a107 to bba8334 Compare November 21, 2025 05:01
@tchaikov tchaikov requested a review from Copilot November 21, 2025 05:01
Copilot finished reviewing on behalf of tchaikov November 21, 2025 05:03
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

@tchaikov tchaikov force-pushed the protect-post-unbounded-alloc branch 3 times, most recently from 1f2f7f8 to 186e522 Compare November 21, 2025 05:59
@tchaikov tchaikov requested a review from Copilot November 21, 2025 05:59
@tchaikov tchaikov force-pushed the protect-post-unbounded-alloc branch from 186e522 to 870071f Compare November 21, 2025 06:00
@Nugine Nugine self-requested a review November 21, 2025 06:02
Copilot finished reviewing on behalf of tchaikov November 21, 2025 06:03
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.

@tchaikov tchaikov force-pushed the protect-post-unbounded-alloc branch from 870071f to 9568125 Compare November 21, 2025 06:14
@tchaikov tchaikov requested a review from Copilot November 21, 2025 07:42
Copilot finished reviewing on behalf of tchaikov November 21, 2025 07:46
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

…s-project#370)

Add defense-in-depth limits to multipart/form-data parsing for POST object
to prevent DoS attacks via oversized uploads:

- MAX_FORM_FIELD_SIZE: 1 MB per field (matching MinIO)
- MAX_FORM_FIELDS_SIZE: 20 MB total for all form fields
- MAX_FORM_PARTS: 1000 maximum parts
- MAX_POST_OBJECT_FILE_SIZE: 5 GB for file content (matching S3 single PUT limit)

Why buffering instead of streaming:

A pure streaming solution using FileStream was initially attempted, but it
breaks compatibility with s3s-proxy and other consumers that use the AWS SDK.
The AWS SDK requires a known Content-Length to compute SHA-256 checksums for
SigV4 request signing. Since FileStream returns an unknown remaining length,
the SDK throws UnsizedRequestBody errors.

MinIO can accept streaming POST uploads directly because its HTTP server
parses multipart boundaries at the protocol level without needing size upfront.
However, when requests are proxied through s3s-proxy using the AWS SDK, the
SDK's cryptographic signing algorithm requires the body size before transmission.

The buffering approach with size limits provides:
1. Protection against unbounded memory allocation (DoS mitigation)
2. Compatibility with AWS SDK-based consumers (s3s-proxy, etc.)
3. Known content length for downstream request signing

Fixes s3s-project#370
Signed-off-by: Kefu Chai <[email protected]>
@tchaikov tchaikov force-pushed the protect-post-unbounded-alloc branch from 9568125 to c36c22a Compare November 21, 2025 08:11
@Nugine
Copy link
Collaborator

Nugine commented Nov 23, 2025

We should support streaming ideally, even if it breaks stream consumers like s3s-proxy. There may be other ways to pass length information.
Limited buffering is still a good start for solving DoS risks. Thank you!
I'll review all changes today.

Copy link
Collaborator

@Nugine Nugine left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Nugine Nugine enabled auto-merge (squash) November 26, 2025 15:58
@Nugine Nugine merged commit 2c20f0b into s3s-project:main Nov 26, 2025
21 of 22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unbounded Memory Allocation in POST object

2 participants