Skip to content

Presigned PUT fails with application/x-www-form-urlencoded because the request body is parsed as a form #1182

Description

@danuo2011

Title:

Presigned PUT fails with application/x-www-form-urlencoded because the request body is parsed as a form

Body:

## Summary

A SigV4 presigned `PUT` can fail with `400 Bad Request` when the client sends a raw binary body with:

```http
Content-Type: application/x-www-form-urlencoded

This is a valid object metadata value from S3's perspective: it should not change how the PUT request body is consumed. MinIO accepts the same request.

Environment

  • S3Proxy: current 4.2.0-SNAPSHOT
  • Authentication: aws-v2-or-v4
  • Blob store: filesystem (the problem appears to be in the HTTP request handling layer)
  • Client: browser upload using a SigV4 presigned URL

Reproduction

  1. Generate a SigV4 presigned URL for PUT Object with only host in X-Amz-SignedHeaders.

  2. Upload arbitrary binary data to that URL while sending:

    Content-Type: application/x-www-form-urlencoded

    For example:

    curl -X PUT "$PRESIGNED_URL" \
      -H 'Content-Type: application/x-www-form-urlencoded' \
      --data-binary @sample.bin

Actual behavior

S3Proxy returns 400 Bad Request. The request reaches S3Proxy successfully; the failure is not caused by DNS, reverse-proxy routing, or a presigned-host mismatch.

Expected behavior

S3Proxy should consume and store the raw PUT body successfully. Content-Type should be treated as object metadata, not as an instruction to parse the request body as an HTML form.

Suspected cause

S3ProxyHandler calls HttpServletRequest#getParameter(...) early for authentication and request routing. With application/x-www-form-urlencoded, the servlet container may parse or consume the raw request body as form data before S3Proxy reads the object stream, causing the subsequent PUT to fail.

Temporary workaround

For an nginx Ingress, clearing this particular request header before proxying makes the upload succeed:

if ($request_method = PUT) { set $clear_content_type 1; }
if ($http_content_type != "application/x-www-form-urlencoded") { set $clear_content_type 0; }
if ($clear_content_type = 1) { more_clear_input_headers "Content-Type"; }

This is only a workaround. It discards the object's supplied content type and requires a specific proxy configuration.

Suggested direction

For PUT and multipart-part PUT requests, read S3 operation and authentication parameters from the URL query string without invoking servlet form-parameter parsing. Keep form parsing only where it is required for S3 POST Policy (multipart/form-data) uploads.

Regression coverage should include ordinary presigned PUTs and multipart UploadPart requests with this content type, while preserving POST Policy behavior.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions