storage: SignedUrlBuilder leaves '*' unencoded in the canonical query, so GCS rejects the URL (SignatureDoesNotMatch)
Maintainers usually reply within 1 day
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 74/100
Research direction
Start in src/storage/src/storage/signed_url.rs at the canonical_query block in sign_with, then compare its encoding with the V4 canonical request requirements linked in the issue. Add coverage for a query key or value containing * and run the relevant storage tests; done means the canonical query encodes * as %2A and the regression test passes.
Written by the indexing model from the issue text.
Description
Problem
SignedUrlBuilder produces a URL that GCS rejects with 403 SignatureDoesNotMatch whenever a query parameter key or value contains *.
The canonical query string is built with url::form_urlencoded, which treats * as safe and leaves it unencoded:
https://github.com/googleapis/google-cloud-rust/blob/main/src/storage/src/storage/signed_url.rs (the canonical_query block in sign_with)
let mut canonical_query = url::form_urlencoded::Serializer::new("".to_string());
for (k, v) in &query_parameters {
canonical_query.append_pair(k, v);
}
canonical_query
.finish()
.replace("%7E", "~") // rollback to ~
.replace("+", "%20") // missing %20 in +
The V4 canonical request spec requires percent-encoding everything outside the RFC 3986 unreserved set (A-Z a-z 0-9 - . _ ~). GCS therefore canonicalizes * as %2A, and the signatures differ. The two existing .replace calls already patch over the same form_urlencoded mismatch for ~ and space; * is the remaining one.
Repro
The standard RFC 6266 / 5987 way to give a download a UTF-8 file name hits this:
let url = SignedUrlBuilder::for_object("projects/_/buckets/my-bucket", "some/object")
.with_query_param(
"response-content-disposition",
"inline; filename=\"report.pdf\"; filename*=UTF-8''report.pdf",
)
.sign_with(&signer)
.await?;
// GET url -> 403 SignatureDoesNotMatch
The CanonicalRequest in the 403 body shows filename%2A%3DUTF-8%27%27report.pdf, while the SDK signed filename*%3DUTF-8%27%27report.pdf.
Seen with google-cloud-storage 1.17.0. The same code is present in 1.20.0.
Proposed fix
Percent-encode * in the canonical query, the same way ~ and + are already handled. For example, add .replace("*", "%2A"), or build the query with percent_encoding and an RFC 3986 unreserved set. #7118 proposes that set for path encoding, and both could share it.
Workaround
Avoid * in signed query parameters. For Content-Disposition, drop filename* and use only an ASCII filename="...".
- Dominant language
- Rust
- Stars
- 963
- Forks
- 151
- Avg merge
- 1d 26m
- Merged PRs (30d)
- 267
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from googleapis/google-cloud-rust
-
type: bug
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
googleapis/google-cloud-rust#6714 ·
Maintainers usually reply within 1 day
-
type: bug
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
googleapis/google-cloud-rust#6713 ·
Maintainers usually reply within 1 day
-
type: bug
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
googleapis/google-cloud-rust#6712 ·
Maintainers usually reply within 1 day
-
type: bug
Difficulty 2/5 Half a day Newbie friendliness 75/100
googleapis/google-cloud-rust#6710 ·
Maintainers usually reply within 1 day
-
type: bug
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
googleapis/google-cloud-rust#6709 ·
Maintainers usually reply within 1 day
All issues in googleapis/google-cloud-rust
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 82/100
521xueweihan/HelloGitHub#4023 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
cloudflare/pingora#1055 ·
Maintainers usually reply within 1 day
-
enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
Maintainers usually reply within 1 day