Skip to content

Commit ac73152

Browse files
committed
Raise an error if trusted_hosts isn't set, with override flag
1 parent 8b7fe7f commit ac73152

2 files changed

Lines changed: 48 additions & 3 deletions

File tree

tests/test_auth_decorator.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ def __init__(self, cls=AuthDecorator, *args, **kwargs):
140140
self.app.testing = True
141141
self.app.secret_key = str(random.random())
142142

143+
kwargs.setdefault("can_trust_request_host", True)
144+
143145
self.authdecorator = cls(*args, **kwargs)
144146
view = self.authdecorator(self.decorated)
145147

@@ -574,8 +576,8 @@ def test_require_combo(self):
574576

575577
def test_custom_check_auth(self):
576578
class MyAuthDecorator(AuthDecorator):
577-
def __init__(self):
578-
AuthDecorator.__init__(self)
579+
def __init__(self, *args, **kwargs):
580+
AuthDecorator.__init__(self, *args, **kwargs)
579581
self._expect_check = []
580582

581583
def check_authorised(self, principal, ptags):
@@ -934,3 +936,19 @@ def test_params_token_doesnt_break_multiple_auths(self):
934936
self.os.set_urandom("ffff")
935937
response = client.get("/decorated", follow_redirects=True)
936938
assert response.status_code == 200
939+
940+
def test_trusted_hosts(self):
941+
self.check_auth(TestRig())
942+
943+
rig = TestRig(can_trust_request_host=False)
944+
class R(flask.Request):
945+
trusted_hosts = {'localhost'}
946+
rig.app.request_class = R
947+
self.check_auth(rig)
948+
949+
rig = TestRig(can_trust_request_host=False)
950+
with rig as (client, wls, views):
951+
wls.expect({}, make_response(principal='djr61'))
952+
953+
assert_raises(RuntimeError,
954+
client.get, "/decorated", follow_redirects=True)

ucam_webauth/flask_glue.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,22 @@ def my_view():
6060
Note that since it uses flask.session, you'll need to set
6161
:attr:`app.secret_key`.
6262
63+
We need to be able to reliably determine the hostname of the current
64+
website. This is retrieved from :attr:`flask.Request.url`.
65+
By default, Werkzeug will respect the value of a ``X-Forwarded-Host``
66+
header, which means that a man-in-the-middle can have someone authenticate
67+
to *their* website, and forward the response from the WLS on to you.
68+
You must either set :attr:`flask.Request.trusted_hosts`, for example
69+
like so::
70+
71+
class R(flask.Request):
72+
trusted_hosts = {'www.danielrichman.co.uk'}
73+
app.request_class = R
74+
75+
... or sanitise both the `Host` header *and* the `X-Forwarded-Host`
76+
header in your web-server. If you choose the second option, set
77+
`can_trust_request_host`.
78+
6379
This tries to emulate the feel of applying mod_ucam_webauth to a file.
6480
6581
The decorator wraps the view in a function that calls
@@ -106,6 +122,10 @@ def my_view():
106122
:type require_ptags: :class:`set` of :class:`str`, or ``None``
107123
:param require_ptags: require the ptags to contain `any` string in
108124
`require_ptags` (i.e., non empty intersection)
125+
:type can_trust_request_host: :class:`bool`
126+
:param can_trust_request_host: Can we trust the hostname in
127+
``request.url``?
128+
(see :ref:`checking-response-values`)
109129
110130
More complex customisation is possible:
111131
@@ -149,7 +169,8 @@ def __init__(self, desc=None, aauth=None, iact=None, msg=None,
149169
max_life=7200, use_wls_life=False,
150170
inactive_timeout=None, issue_bounds=(15,5),
151171
require_principal=None,
152-
require_ptags=frozenset(["current"])):
172+
require_ptags=frozenset(["current"]),
173+
can_trust_request_host=False):
153174
self.desc = desc
154175
self.aauth = aauth
155176
self.iact = iact
@@ -160,6 +181,7 @@ def __init__(self, desc=None, aauth=None, iact=None, msg=None,
160181
self.issue_bounds = issue_bounds
161182
self.require_principal = require_principal
162183
self.require_ptags = require_ptags
184+
self.can_trust_request_host = can_trust_request_host
163185

164186
def __call__(self, view_function):
165187
"""
@@ -423,6 +445,11 @@ def _check_url(self, wls_response_url):
423445
redirected after authentication.
424446
"""
425447

448+
if not self.can_trust_request_host and request.trusted_hosts is None:
449+
raise RuntimeError("Either set request.trusted_hosts, or sanitise "
450+
"Host/X-Forwarded-Host headers and pass "
451+
"can_trust_request_host = True")
452+
426453
actual_url = request.url
427454

428455
# note: mod_ucam_webauth simply strips everything up to a ?

0 commit comments

Comments
 (0)