|
15 | 15 | import hmac
|
16 | 16 | import importlib
|
17 | 17 | import io
|
| 18 | +import ipaddress |
18 | 19 | import json
|
19 | 20 | import logging
|
20 | 21 | import mimetypes
|
@@ -252,6 +253,8 @@ def init_settings(self, jupyter_app, kernel_manager, contents_manager,
|
252 | 253 | password=jupyter_app.password,
|
253 | 254 | xsrf_cookies=True,
|
254 | 255 | disable_check_xsrf=jupyter_app.disable_check_xsrf,
|
| 256 | + allow_remote_access=jupyter_app.allow_remote_access, |
| 257 | + local_hostnames=jupyter_app.local_hostnames, |
255 | 258 |
|
256 | 259 | # managers
|
257 | 260 | kernel_manager=kernel_manager,
|
@@ -831,6 +834,46 @@ def _token_changed(self, change):
|
831 | 834 | """
|
832 | 835 | )
|
833 | 836 |
|
| 837 | + allow_remote_access = Bool(config=True, |
| 838 | + help="""Allow requests where the Host header doesn't point to a local server |
| 839 | +
|
| 840 | + By default, requests get a 403 forbidden response if the 'Host' header |
| 841 | + shows that the browser thinks it's on a non-local domain. |
| 842 | + Setting this option to True disables this check. |
| 843 | +
|
| 844 | + This protects against 'DNS rebinding' attacks, where a remote web server |
| 845 | + serves you a page and then changes its DNS to send later requests to a |
| 846 | + local IP, bypassing same-origin checks. |
| 847 | +
|
| 848 | + Local IP addresses (such as 127.0.0.1 and ::1) are allowed as local, |
| 849 | + along with hostnames configured in local_hostnames. |
| 850 | + """) |
| 851 | + |
| 852 | + @default('allow_remote_access') |
| 853 | + def _default_allow_remote(self): |
| 854 | + """Disallow remote access if we're listening only on loopback addresses""" |
| 855 | + try: |
| 856 | + addr = ipaddress.ip_address(self.ip) |
| 857 | + except ValueError: |
| 858 | + # Address is a hostname |
| 859 | + for info in socket.getaddrinfo(self.ip, self.port, 0, socket.SOCK_STREAM): |
| 860 | + addr = info[4][0] |
| 861 | + if not py3compat.PY3: |
| 862 | + addr = addr.decode('ascii') |
| 863 | + if not ipaddress.ip_address(addr).is_loopback: |
| 864 | + return True |
| 865 | + return False |
| 866 | + else: |
| 867 | + return not addr.is_loopback |
| 868 | + |
| 869 | + local_hostnames = List(Unicode(), ['localhost'], config=True, |
| 870 | + help="""Hostnames to allow as local when allow_remote_access is False. |
| 871 | +
|
| 872 | + Local IP addresses (such as 127.0.0.1 and ::1) are automatically accepted |
| 873 | + as local as well. |
| 874 | + """ |
| 875 | + ) |
| 876 | + |
834 | 877 | open_browser = Bool(True, config=True,
|
835 | 878 | help="""Whether to open in a browser after starting.
|
836 | 879 | The specific browser used is platform dependent and
|
|
0 commit comments