@@ -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