In-app reader
This is a third follow-up to CVE-2024-42353 / GHSA-mg3v-6m49-jhp3
and CVE-2026-44889 / GHSA-fh3h-vg37-cc95.
WebOb makes the Location header absolute when it serves a redirect. To stop a
relative or protocol-relative target from redirecting users off-host, it checks
the value for a URI scheme and for a leading //, then joins it against the
request URI with urllib.parse.urljoin(). The previous fix additionally stripped
ASCII tab/CR/LF from the value before those checks.
However, on Python 3.10+ urllib.parse.urljoin() (via urlsplit()) does more
than remove tab/CR/LF: **it also strips leading and trailing C0 control
characters (U+0000–U+001F) and spaces from the URL before parsing it.**
Because WebOb's guard checks (SCHEME_RE and startswith("//")) run against the
un-stripped value, a single leading space or control byte slips past them, and
urljoin() then silently removes that byte and parses what remains as a
protocol-relative — or even absolute — URL. The result is an open redirect to an
attacker-controlled host.
Response._make_location_absolute() (in src/webob/response.py) performed,
prior to the fix:
value = value.replace("\t", "").replace("\r", "").replace("\n", "")
if SCHEME_RE.search(value): # ^[a-z]+: -> already absolute, return as-is
return value
if value.startswith("//"): # neutralize protocol-relative URLs
value = f"/%2f{value[2:]}"
new_location = urlparse.urljoin(_request_uri(environ), value)
Discussion
Sign in to join the discussion.
Keep reading
Optional: create a free account to save items, track programs, and sync across web + app. Reading stays free.