Check 'Host' header for local connections

Thomas Kluyver 8 years ago
parent b8b66332e2
commit 7f1bba613d

@ -5,6 +5,7 @@
import datetime
import functools
import ipaddress
import json
import mimetypes
import os
@ -411,6 +412,39 @@ class IPythonHandler(AuthenticatedHandler):
return
return super(IPythonHandler, self).check_xsrf_cookie()
def check_host(self):
"""Check the host header if remote access disallowed.
Returns True if the request should continue, False otherwise.
"""
if self.settings.get('allow_remote_access', False):
return True
# Remove port (e.g. ':8888') from host
host = re.match(r'^(.*?)(:\d+)?$', self.request.host).group(1)
# Browsers format IPv6 addresses like [::1]; we need to remove the []
if host.startswith('[') and host.endswith(']'):
host = host[1:-1]
try:
addr = ipaddress.ip_address(host)
except ValueError:
# Not an IP address: check against hostnames
allow = host in self.settings.get('local_hostnames', [])
else:
allow = addr.is_loopback
if not allow:
self.log.warning("Blocking request with non-local 'Host' %s (%s)",
host, self.request.host)
return allow
def prepare(self):
if not self.check_host():
raise web.HTTPError(403)
return super(IPythonHandler, self).prepare()
#---------------------------------------------------------------
# template rendering
#---------------------------------------------------------------

@ -252,6 +252,8 @@ class NotebookWebApplication(web.Application):
password=jupyter_app.password,
xsrf_cookies=True,
disable_check_xsrf=jupyter_app.disable_check_xsrf,
allow_remote_access=jupyter_app.allow_remote_access,
local_hostnames=jupyter_app.local_hostnames,
# managers
kernel_manager=kernel_manager,
@ -831,6 +833,29 @@ class NotebookApp(JupyterApp):
"""
)
allow_remote_access = Bool(False, config=True,
help="""Allow requests where the Host header doesn't point to a local server
By default, requests get a 403 forbidden response if the 'Host' header
shows that the browser thinks it's on a non-local domain.
Setting this option to True disables this check.
This protects against 'DNS rebinding' attacks, where a remote web server
serves you a page and then changes its DNS to send later requests to a
local IP, bypassing same-origin checks.
Local IP addresses (such as 127.0.0.1 and ::1) are allowed as local,
along with hostnames configured in local_hostnames.
""")
local_hostnames = List(Unicode(), ['localhost'], config=True,
help="""Hostnames to allow as local when allow_remote_access is False.
Local IP addresses (such as 127.0.0.1 and ::1) are automatically accepted
as local as well.
"""
)
open_browser = Bool(True, config=True,
help="""Whether to open in a browser after starting.
The specific browser used is platform dependent and

@ -94,6 +94,7 @@ for more information.
'prometheus_client'
],
extras_require = {
':python_version == "2.7"': ['ipaddress'],
'test:python_version == "2.7"': ['mock'],
'test': ['nose', 'coverage', 'requests', 'nose_warnings_filters',
'nbval', 'nose-exclude'],

Loading…
Cancel
Save