From c2cc5f39ca3c64f92fbd849b56bc094baf2cd161 Mon Sep 17 00:00:00 2001 From: Bryan Van de Ven Date: Sun, 3 Jan 2016 16:57:10 -0600 Subject: [PATCH 1/2] accept implicit port 80 on whitelist --- bokeh/server/tests/test_tornado.py | 19 ++++++++++++++++++- bokeh/server/tornado.py | 25 ++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/bokeh/server/tests/test_tornado.py b/bokeh/server/tests/test_tornado.py index bc69ec298d9..3ddfbbc6611 100644 --- a/bokeh/server/tests/test_tornado.py +++ b/bokeh/server/tests/test_tornado.py @@ -1,5 +1,8 @@ from __future__ import absolute_import +import pytest +from tornado.web import HTTPError + import bokeh.server.tornado as tornado class _Handler(object): @@ -17,4 +20,18 @@ def test__whitelist_replaces_prepare_only_once(): tornado._whitelist(h) new_prepare = h.prepare tornado._whitelist(h) - assert h.prepare == new_prepare \ No newline at end of file + assert h.prepare == new_prepare + +def test_check_whitelist_rejects_port_mismatch(): + with pytest.raises(HTTPError): + tornado.check_whitelist("foo:100", ["foo:101", "foo:102"]) + +def test_check_whitelist_rejects_name_mismatch(): + with pytest.raises(HTTPError): + tornado.check_whitelist("foo:100", ["bar:100", "baz:100"]) + +def test_check_whitelist_accepts_name_port_match(): + tornado.check_whitelist("foo:100", ["foo:100", "baz:100"]) + +def test_check_whitelist_accepts_implicit_port_80(): + tornado.check_whitelist("foo", ["foo:80"]) \ No newline at end of file diff --git a/bokeh/server/tornado.py b/bokeh/server/tornado.py index 09b2ebf863b..5ea916ca534 100644 --- a/bokeh/server/tornado.py +++ b/bokeh/server/tornado.py @@ -25,14 +25,33 @@ from .application_context import ApplicationContext from .views.static_handler import StaticHandler +# factored out to be easier to test +def check_whitelist(request_host, whitelist): + ''' Check a given request host against a whitelist. + + ''' + if request_host not in whitelist: + + # see if the request came with no port, assume port 80 in that case + if len(request_host.split(':')) == 1: + host = request_host + ":80" + if host in whitelist: + log.debug("Accepting connection from '%s' because '%s' is in the --host whitelist" % (request_host, host)) + else: + log.info("Rejected connection from host '%s' because it is not in the --host whitelist" % request_host) + raise HTTPError(403) + + else: + log.info("Rejected connection from host '%s' because it is not in the --host whitelist" % request_host) + raise HTTPError(403) + + def _whitelist(handler_class): if hasattr(handler_class.prepare, 'patched'): return old_prepare = handler_class.prepare def _prepare(self, *args, **kw): - if self.request.host not in self.application._hosts: - log.info("Rejected connection from host '%s' because it is not in the --host whitelist" % self.request.host) - raise HTTPError(403) + check_whitelist(self.request.host, self.application._hosts) return old_prepare(self, *args, **kw) _prepare.patched = True handler_class.prepare = _prepare From cb39cca54ed1f02dbd4e91e81fa753e185d40b57 Mon Sep 17 00:00:00 2001 From: Bryan Van de Ven Date: Sun, 3 Jan 2016 17:22:19 -0600 Subject: [PATCH 2/2] need to check ws origins too --- bokeh/server/tests/test_tornado.py | 13 ++++--------- bokeh/server/tornado.py | 16 +++++++--------- bokeh/server/views/ws.py | 6 ++++-- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/bokeh/server/tests/test_tornado.py b/bokeh/server/tests/test_tornado.py index 3ddfbbc6611..d7228d3f191 100644 --- a/bokeh/server/tests/test_tornado.py +++ b/bokeh/server/tests/test_tornado.py @@ -1,8 +1,5 @@ from __future__ import absolute_import -import pytest -from tornado.web import HTTPError - import bokeh.server.tornado as tornado class _Handler(object): @@ -23,15 +20,13 @@ def test__whitelist_replaces_prepare_only_once(): assert h.prepare == new_prepare def test_check_whitelist_rejects_port_mismatch(): - with pytest.raises(HTTPError): - tornado.check_whitelist("foo:100", ["foo:101", "foo:102"]) + assert False == tornado.check_whitelist("foo:100", ["foo:101", "foo:102"]) def test_check_whitelist_rejects_name_mismatch(): - with pytest.raises(HTTPError): - tornado.check_whitelist("foo:100", ["bar:100", "baz:100"]) + assert False == tornado.check_whitelist("foo:100", ["bar:100", "baz:100"]) def test_check_whitelist_accepts_name_port_match(): - tornado.check_whitelist("foo:100", ["foo:100", "baz:100"]) + assert True == tornado.check_whitelist("foo:100", ["foo:100", "baz:100"]) def test_check_whitelist_accepts_implicit_port_80(): - tornado.check_whitelist("foo", ["foo:80"]) \ No newline at end of file + assert True == tornado.check_whitelist("foo", ["foo:80"]) \ No newline at end of file diff --git a/bokeh/server/tornado.py b/bokeh/server/tornado.py index 5ea916ca534..6d2ccae16e4 100644 --- a/bokeh/server/tornado.py +++ b/bokeh/server/tornado.py @@ -35,15 +35,11 @@ def check_whitelist(request_host, whitelist): # see if the request came with no port, assume port 80 in that case if len(request_host.split(':')) == 1: host = request_host + ":80" - if host in whitelist: - log.debug("Accepting connection from '%s' because '%s' is in the --host whitelist" % (request_host, host)) - else: - log.info("Rejected connection from host '%s' because it is not in the --host whitelist" % request_host) - raise HTTPError(403) - + return host in whitelist else: - log.info("Rejected connection from host '%s' because it is not in the --host whitelist" % request_host) - raise HTTPError(403) + return False + + return True def _whitelist(handler_class): @@ -51,7 +47,9 @@ def _whitelist(handler_class): return old_prepare = handler_class.prepare def _prepare(self, *args, **kw): - check_whitelist(self.request.host, self.application._hosts) + if not check_whitelist(self.request.host, self.application._hosts): + log.info("Rejected connection from host '%s' because it is not in the --host whitelist" % self.request.host) + raise HTTPError(403) return old_prepare(self, *args, **kw) _prepare.patched = True handler_class.prepare = _prepare diff --git a/bokeh/server/views/ws.py b/bokeh/server/views/ws.py index f0be930e37f..ee8606977b5 100644 --- a/bokeh/server/views/ws.py +++ b/bokeh/server/views/ws.py @@ -42,12 +42,14 @@ def initialize(self, application_context, bokeh_websocket_path): pass def check_origin(self, origin): + from ..tornado import check_whitelist parsed_origin = urlparse(origin) origin_host = parsed_origin.netloc.lower() - allowed = self.application.websocket_origins + allowed_hosts = self.application.websocket_origins - if origin_host in allowed: + allowed = check_whitelist(origin_host, allowed_hosts) + if allowed: return True else: log.error("Refusing websocket connection from Origin '%s'; use --allow-websocket-origin=%s to permit this; currently we allow origins %r", origin, origin_host, allowed)