Skip to content

Commit 7f7bfee

Browse files
committed
Block cross-origin GET,HEAD requests with mismatched Referer
- /files/ downloads must come from a local page (no direct visits or external links) - same for /api/ requests - disabling xsrf checks
1 parent d7becaf commit 7f7bfee

File tree

1 file changed

+57
-1
lines changed

1 file changed

+57
-1
lines changed

notebook/base/handlers.py

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,13 +400,69 @@ def check_origin(self, origin_to_satisfy_tornado=""):
400400
)
401401
return allow
402402

403+
def check_referer(self):
404+
"""Check Referer for cross-site requests.
405+
406+
Disables requests to certain endpoints with
407+
external or missing Referer.
408+
409+
If set, allow_origin settings are applied to the Referer
410+
to whitelist specific cross-origin sites.
411+
412+
Used on GET for api endpoints and /files/
413+
to block cross-site inclusion (XSSI).
414+
"""
415+
host = self.request.headers.get("Host")
416+
referer = self.request.headers.get("Referer")
417+
418+
if not host:
419+
self.log.warning("Blocking request with no host")
420+
return False
421+
if not referer:
422+
self.log.warning("Blocking request with no referer")
423+
return False
424+
425+
referer_url = urlparse(referer)
426+
referer_host = referer_url.netloc
427+
if referer_host == host:
428+
return True
429+
430+
# apply cross-origin checks to Referer:
431+
origin = "{}://{}".format(referer_url.scheme, referer_url.netloc)
432+
if self.allow_origin:
433+
allow = self.allow_origin == origin
434+
elif self.allow_origin_pat:
435+
allow = bool(self.allow_origin_pat.match(origin))
436+
else:
437+
# No CORS settings, deny the request
438+
allow = False
439+
440+
if not allow:
441+
self.log.warning("Blocking Cross Origin request for %s. Referer: %s, Host: %s",
442+
self.request.path, origin, host,
443+
)
444+
return allow
445+
403446
def check_xsrf_cookie(self):
404447
"""Bypass xsrf cookie checks when token-authenticated"""
405448
if self.token_authenticated or self.settings.get('disable_check_xsrf', False):
406449
# Token-authenticated requests do not need additional XSRF-check
407450
# Servers without authentication are vulnerable to XSRF
408451
return
409-
return super(IPythonHandler, self).check_xsrf_cookie()
452+
try:
453+
return super(IPythonHandler, self).check_xsrf_cookie()
454+
except web.HTTPError as e:
455+
if self.request.method in {'GET', 'HEAD'}:
456+
# Consider Referer a sufficient cross-origin check for GET requests
457+
if not self.check_referer():
458+
referer = self.request.headers.get('Referer')
459+
if referer:
460+
msg = "Blocking Cross Origin request from {}.".format(referer)
461+
else:
462+
msg = "Blocking request from unknown origin"
463+
raise web.HTTPError(403, msg)
464+
else:
465+
raise
410466

411467
def check_host(self):
412468
"""Check the host header if remote access disallowed.

0 commit comments

Comments
 (0)