Skip to content

fix(sigv4): use encoded request path for inbound signature verification#176

Open
alukach wants to merge 1 commit into
mainfrom
fix/sigv4-encoded-key-path
Open

fix(sigv4): use encoded request path for inbound signature verification#176
alukach wants to merge 1 commit into
mainfrom
fix/sigv4-encoded-key-path

Conversation

@alukach

@alukach alukach commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Problem

Uploading an object whose key contains a character the client percent-escapes — e.g. a space (foo barfoo%20bar) — fails with 403 SignatureDoesNotMatch:

[WARN] multistore::auth::sigv4: SigV4 signature mismatch
canonical_request=PUT
/alukach/upload-tests/foo bar     <-- literal space
...

Root cause

SigV4's canonical URI is the percent-encoded path the client signed. RequestParts::from_web_sys decodes the request path (correct for bucket/key routing — operation.key() needs the decoded foo bar), and we were passing that same decoded path as the signing_path. So the server rebuilt its canonical request with a literal space while the client (aws-sdk-js) signed %20 → signatures never match.

The outbound leg was already correct: build_backend_url hands the decoded key to url::Url::parse, which re-encodes the space to %20 consistently for both the forwarded request and its signature.

Fix

Recover the raw encoded path from the request URL via http::Uri::path() (which does not decode) and use it as the signing path. Routing/bucket-key parsing keeps the decoded path. Handles all escaped characters, not just spaces; falls back to the decoded path if the URL won't parse.

Tests

  • New characterization test in tests/routing.rs pinning the http behavior the fix relies on (Uri::path() preserves %20).
  • Full native suite + wasm clippy/check pass via the pre-commit hook.

⚠️ Verified locally only (the fetch handler is wasm-only and can't be unit-tested natively). Needs a staging deploy to confirm the real foo bar upload now succeeds.

🤖 Generated with Claude Code

Uploads of keys containing characters the client percent-escapes (e.g. a
space -> %20) failed with SignatureDoesNotMatch. SigV4's canonical URI is
the percent-encoded path the client signed, but `RequestParts` decodes the
path (correct for bucket/key routing) and we were passing that decoded path
as the `signing_path`. The server then built its canonical request with a
literal space while the client signed `%20`, so the signatures never matched.

Recover the raw encoded path from the request URL via `Uri::path()` (which
does not decode) and use it as the signing path. Routing keeps the decoded
path; outbound signing already re-encodes via `url::Url::parse`, so it was
unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Claude finished @alukach's task in 4m 13s —— View job


Reviewing PR #176

  • Gather context (diff, changed files)
  • Review src/lib.rs changes
  • Review tests/routing.rs changes
  • Post final review

@alukach alukach deployed to preview July 1, 2026 03:05 — with GitHub Actions Active
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

🚀 Latest commit deployed to https://source-data-proxy-pr-176.source-coop.workers.dev

  • Date: 2026-07-01T03:05:20Z
  • Commit: 4a82658

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant