From 7f7bfeefc08568aacd626438e22a6361aa57223a Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 31 Jan 2019 15:54:50 +0100 Subject: [PATCH] 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 --- notebook/base/handlers.py | 58 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/notebook/base/handlers.py b/notebook/base/handlers.py index b458445b14..cd801c9a10 100755 --- a/notebook/base/handlers.py +++ b/notebook/base/handlers.py @@ -400,13 +400,69 @@ def check_origin(self, origin_to_satisfy_tornado=""): ) return allow + def check_referer(self): + """Check Referer for cross-site requests. + + Disables requests to certain endpoints with + external or missing Referer. + + If set, allow_origin settings are applied to the Referer + to whitelist specific cross-origin sites. + + Used on GET for api endpoints and /files/ + to block cross-site inclusion (XSSI). + """ + host = self.request.headers.get("Host") + referer = self.request.headers.get("Referer") + + if not host: + self.log.warning("Blocking request with no host") + return False + if not referer: + self.log.warning("Blocking request with no referer") + return False + + referer_url = urlparse(referer) + referer_host = referer_url.netloc + if referer_host == host: + return True + + # apply cross-origin checks to Referer: + origin = "{}://{}".format(referer_url.scheme, referer_url.netloc) + if self.allow_origin: + allow = self.allow_origin == origin + elif self.allow_origin_pat: + allow = bool(self.allow_origin_pat.match(origin)) + else: + # No CORS settings, deny the request + allow = False + + if not allow: + self.log.warning("Blocking Cross Origin request for %s. Referer: %s, Host: %s", + self.request.path, origin, host, + ) + return allow + def check_xsrf_cookie(self): """Bypass xsrf cookie checks when token-authenticated""" if self.token_authenticated or self.settings.get('disable_check_xsrf', False): # Token-authenticated requests do not need additional XSRF-check # Servers without authentication are vulnerable to XSRF return - return super(IPythonHandler, self).check_xsrf_cookie() + try: + return super(IPythonHandler, self).check_xsrf_cookie() + except web.HTTPError as e: + if self.request.method in {'GET', 'HEAD'}: + # Consider Referer a sufficient cross-origin check for GET requests + if not self.check_referer(): + referer = self.request.headers.get('Referer') + if referer: + msg = "Blocking Cross Origin request from {}.".format(referer) + else: + msg = "Blocking request from unknown origin" + raise web.HTTPError(403, msg) + else: + raise def check_host(self): """Check the host header if remote access disallowed.