mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-19 07:53:00 +00:00
use outermost proxied entry when checking for browser protocol
wee care about what the browser sees, so trust the outermost entry instead of the innermost This is not secure _in general_, in that these values can be spoofed by malicious proxies, but for CORS and cookie purposes, we only care about what the browser sees, however many hops there may be. A malicious proxy in the chain here isn't a concern because what matters is the immediate hop from the _browser_, not the immediate hop from the _server_.
This commit is contained in:
@@ -683,3 +683,44 @@ def catch_db_error(f):
|
||||
return r
|
||||
|
||||
return catching
|
||||
|
||||
|
||||
def get_browser_protocol(request):
|
||||
"""Get the _protocol_ seen by the browser
|
||||
|
||||
Like tornado's _apply_xheaders,
|
||||
but in the case of multiple proxy hops,
|
||||
use the outermost value (what the browser likely sees)
|
||||
instead of the innermost value,
|
||||
which is the most trustworthy.
|
||||
|
||||
We care about what the browser sees,
|
||||
not where the request actually came from,
|
||||
so trusting possible spoofs is the right thing to do.
|
||||
"""
|
||||
headers = request.headers
|
||||
# first choice: Forwarded header
|
||||
forwarded_header = headers.get("Forwarded")
|
||||
if forwarded_header:
|
||||
first_forwarded = forwarded_header.split(",", 1)[0].strip()
|
||||
fields = {}
|
||||
forwarded_dict = {}
|
||||
for field in first_forwarded.split(";"):
|
||||
key, _, value = field.partition("=")
|
||||
fields[key.strip().lower()] = value.strip()
|
||||
if "proto" in fields and fields["proto"].lower() in {"http", "https"}:
|
||||
return fields["proto"].lower()
|
||||
else:
|
||||
app_log.warning(
|
||||
f"Forwarded header present without protocol: {forwarded_header}"
|
||||
)
|
||||
|
||||
# second choice: X-Scheme or X-Forwarded-Proto
|
||||
proto_header = headers.get("X-Scheme", headers.get("X-Forwarded-Proto", None))
|
||||
if proto_header:
|
||||
proto_header = proto_header.split(",")[0].strip().lower()
|
||||
if proto_header in {"http", "https"}:
|
||||
return proto_header
|
||||
|
||||
# no forwarded headers
|
||||
return request.protocol
|
||||
|
Reference in New Issue
Block a user