Skip to content

web: Harden RedirectHandler - #3786

Merged
bdarnell merged 3 commits into
tornadoweb:masterfrom
bdarnell:claude/confident-pascal-putsl0
Oct 10, 2026
Merged

bdarnell merged 3 commits into
tornadoweb:masterfrom
bdarnell:claude/confident-pascal-putsl0

Conversation

@bdarnell

Copy link
Copy Markdown
Member

The previous fix to avoid open redirects via protocol-relative URLs missed some cases due to the way browsers handle backslashes and whitespace in URLs. The first commit in this branch fixes that, and the second generalizes the open-redirect protection by ensuring that the scheme and netloc of the destination url come from the template and not the request.

…irects

The open-redirect checks in RedirectHandler and StaticFileHandler only
looked for a literal leading "//". Browsers follow the WHATWG URL spec,
which treats backslashes like slashes and drops tabs and newlines, so
Location headers such as "/\evil.com" or "/<TAB>/evil.com" are also
protocol-relative redirects to another host.

RedirectHandler was exploitable this way because path arguments are
url-decoded before they are substituted into the template, so a link
to e.g. /%5Cevil.com/... produced a raw backslash in the redirect.
StaticFileHandler checks the undecoded request path, which a browser
cannot make contain a raw backslash, but harden it the same way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jhn71YM65mTQ8sdDrEMwS8
Rather than looking only for protocol-relative URLs, RedirectHandler now
requires the scheme and netloc (user info, host, and port) of the
redirect to be the same as those of the template formatted with
placeholder values. This also covers templates like
"https://example.com{0}", where a substitution could extend the host
(".evil.com") or add user info ("@evil.com"), and templates beginning
with a substitution, which could otherwise be given an absolute URL.
Such requests, and protocol-relative ones, now get a 400 response
instead of a 500 from an uncaught ValueError.

A consequence is that substitutions can no longer be used in the scheme
or netloc part of a template.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jhn71YM65mTQ8sdDrEMwS8
On Windows a leading backslash makes the static path absolute, so the
request is rejected with a 403 by the root directory check before it
reaches the redirect check. The request is still blocked; only the log
message differs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jhn71YM65mTQ8sdDrEMwS8
@bdarnell
bdarnell merged commit f53b9b9 into tornadoweb:master Oct 10, 2026
17 checks passed
@bdarnell
bdarnell deleted the claude/confident-pascal-putsl0 branch October 10, 2026 18:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants