From 17e3a8f942d5b30ba7ae2a2b36e42b108202bd8f Mon Sep 17 00:00:00 2001 From: Nigel Kukard Date: Tue, 14 Mar 2023 23:48:25 +0000 Subject: [PATCH] fix(auth:basic): Basic auth exception handling improvement Currently passing an invalid Basic auth header (random string base64 encoded) would result in an exception being raised due to a username, password = auth_header.split(). Similary passing a `Digest` authentication type would result in an exception as there is no :. Thirdly passing invalid base64 encoded UTF-8 code sequences would result in exceptions as this issue (#1424). I added code to check explicitly that we are doing basic authentication then by checking the number of entries returned by the split. I also added exception handling for invalid UTF-8 code sequence exceptions. Tested with a fuzzer. Tested with valid and invalid credentials. This fixes #1424. --- powerdnsadmin/routes/base.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/powerdnsadmin/routes/base.py b/powerdnsadmin/routes/base.py index 7af342c..f805c90 100644 --- a/powerdnsadmin/routes/base.py +++ b/powerdnsadmin/routes/base.py @@ -60,15 +60,31 @@ def login_via_authorization_header_or_remote_user(request): # Try to login using Basic Authentication auth_header = request.headers.get('Authorization') if auth_header: + + if auth_header[:6] != "Basic ": + return None + auth_method = request.args.get('auth_method', 'LOCAL') auth_method = 'LDAP' if auth_method != 'LOCAL' else 'LOCAL' - auth_header = auth_header.replace('Basic ', '', 1) + + # Remove "Basic " from the header value + auth_header = auth_header[6:] + try: auth_header = str(base64.b64decode(auth_header), 'utf-8') - username, password = auth_header.split(":") - except TypeError as e: + except (UnicodeDecodeError, TypeError) as e: return None + # NK: We use auth_components here as we don't know if we'll have a :, we split it maximum 1 times to grab the + # username, the rest of the string would be the password. + auth_components = auth_header.split(':', maxsplit=1) + + # If we don't have two auth components (username, password), we can return + if len(auth_components) != 2: + return None + + (username, password) = auth_components + user = User(username=username, password=password, plain_text_password=password)