Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 11, 07:15 UTC
rewrite: fix URI splitting when a query or fragment arrives via a placeholder (#7947)
* rewrite: don't drop a trailing '=' from the query string buildQueryString scanned for '=' unconditionally when looking for the end of a component, but '=' only delimits a key from its value once; any further '=' bytes are literal data. When the query ended with '=', that byte was consumed as a delimiter with nothing following it to re-emit, so it was silently lost: ?x=1&sig=YWJjZA== => ?x=1&sig=YWJjZA= This corrupts base64 padding in the last query parameter, which is the shape of an S3 presigned URL (X-Amz-Signature), turning a valid signature into a 403. Only the final '=' of the query was affected; ?sig=YWJjZA==&x=1 came through intact. Disable the '=' search while consuming a value so that only '&' ends it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * rewrite: honor a query string injected by a replacement value Which URI components get written back was decided from the literal config string, before placeholders were expanded, but an injected query is only detected after expansion. The two were never reconciled: for `rewrite * {rp.header.X-Accel-Redirect}` there is no literal '?', so qsStart stayed -1, and the correctly-built query string was computed and then discarded by the `if qsStart >= 0` guard. Only half the split was applied. The path was still truncated at the injected '?', so the query was not preserved either -- it was dropped, and any query already on the request survived in its place: GET /orig?keep=me, X-Accel-Redirect: /hello?some=param => /hello?keep=me Track whether a query was actually injected and include that in the write-back condition. Appending a literal '?' to the rewrite value was the known workaround precisely because it set qsStart; that keeps working and is now unnecessary. The flag is only set where the injected query is adopted, so an explicitly configured query still wins, and a value with no '?' still leaves the query untouched -- which is what the implicit rewrites of try_files and php_fastcgi rely on. Those stay safe regardless, since escapePathPlaceholders already escapes the two placeholders they use, so a client-supplied %3F cannot split the URI. Fixes #5208 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * rewrite: drop a fragment injected by a replacement value The scan that separates path, query and fragment runs on the literal config string, so a '#' arriving later via a replacement value was never treated as a delimiter. It leaked into whichever component it landed in: X-Accel-Redirect: /hello?p=x#frag => RawQuery = "p=x#frag" Everything after '#' is fragment (RFC 3986 section 4.2) and a fragment is never sent to the server, so drop it before the path is split, mirroring how the scan already handles a literal '#'. An escaped %23 is unaffected, so a real '#' in a path or query is still expressible, and a configured fragment still wins over an injected one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>