From 5279b3985d1bf26c980603aabaca67b6a1e4a295 Mon Sep 17 00:00:00 2001 From: GauthierGitLab Date: Tue, 29 Oct 2024 14:26:21 +0100 Subject: [PATCH 1/5] Fix open redirect with backslash URL --- flask_multipass/core.py | 3 ++- tests/test_core.py | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/flask_multipass/core.py b/flask_multipass/core.py index 62f729a..920e2a8 100644 --- a/flask_multipass/core.py +++ b/flask_multipass/core.py @@ -135,7 +135,8 @@ def validate_next_url(self, url): a whitelist of trusted hosts to avoid creating an open redirector. """ url_info = urlsplit(url) - if url_info.scheme and url_info.scheme not in {'http', 'https'}: + if (url_info.scheme and url_info.scheme not in {'http', 'https'})\ + or (url_info.scheme in {'http', 'https'} and not url_info.netloc): return False return not url_info.netloc or url_info.netloc == request.host diff --git a/tests/test_core.py b/tests/test_core.py index 12e1403..7715ee0 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -161,6 +161,10 @@ def test_next_url_invalid(): ('//evil.com:80', False), ('http://evil.com', False), ('https://evil.com', False), + ('http:\\\\evil.com', False), + ('http:\\evil.com', False), + ('https:\\\\evil.com', False), + ('https:\\evil.com', False), ('javascript:alert("eeeeeeeevil")', False), )) def test_validate_next_url(url, valid): From 8641d56bfb6deefb98213b313249c6d79a62dfd6 Mon Sep 17 00:00:00 2001 From: GauthierGitLab Date: Tue, 29 Oct 2024 15:09:19 +0100 Subject: [PATCH 2/5] Chore improve readability on core.validate_next_url --- flask_multipass/core.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/flask_multipass/core.py b/flask_multipass/core.py index 920e2a8..a09e62a 100644 --- a/flask_multipass/core.py +++ b/flask_multipass/core.py @@ -135,8 +135,9 @@ def validate_next_url(self, url): a whitelist of trusted hosts to avoid creating an open redirector. """ url_info = urlsplit(url) - if (url_info.scheme and url_info.scheme not in {'http', 'https'})\ - or (url_info.scheme in {'http', 'https'} and not url_info.netloc): + if url_info.scheme and url_info.scheme not in {'http', 'https'}: + return False + if url_info.scheme in {'http', 'https'} and not url_info.netloc: return False return not url_info.netloc or url_info.netloc == request.host From 39d64a00fc0d1b9d1f04e75dcca647b2873a1212 Mon Sep 17 00:00:00 2001 From: Adrian Date: Tue, 29 Oct 2024 16:51:38 +0100 Subject: [PATCH 3/5] Update tests/test_core.py --- tests/test_core.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index 7715ee0..682b1b7 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -161,10 +161,10 @@ def test_next_url_invalid(): ('//evil.com:80', False), ('http://evil.com', False), ('https://evil.com', False), - ('http:\\\\evil.com', False), - ('http:\\evil.com', False), - ('https:\\\\evil.com', False), - ('https:\\evil.com', False), + (r'http:\\evil.com', False), + (r'http:\evil.com', False), + (r'https:\\evil.com', False), + (r'https:\evil.com', False), ('javascript:alert("eeeeeeeevil")', False), )) def test_validate_next_url(url, valid): From 241df1cfb2cd5e4a58da1391f7ec2b08adb92c0b Mon Sep 17 00:00:00 2001 From: Adrian Moennich Date: Tue, 29 Oct 2024 16:53:29 +0100 Subject: [PATCH 4/5] Update changelog + bump version --- CHANGES.rst | 6 ++++++ pyproject.toml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 3c1ea89..184fb53 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,12 @@ Changelog ========= +Version 0.5.6 +------------- + +- Reject invalid ``next`` URLs with backslashes that could be used to trick browsers into + redirecting to an otherwise disallowed host when doing client-side redirects + Version 0.5.5 ------------- diff --git a/pyproject.toml b/pyproject.toml index ad0584c..eb5ad2c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = 'Flask-Multipass' -version = '0.5.5' +version = '0.5.6' description = 'A pluggable solution for multi-backend authentication with Flask' readme = 'README.rst' license = 'BSD-3-Clause' From fcb6b3f627a76163de02ae64cee2a5462bd52b00 Mon Sep 17 00:00:00 2001 From: Adrian Moennich Date: Tue, 29 Oct 2024 17:14:22 +0100 Subject: [PATCH 5/5] Handle more cases of weird slashes --- flask_multipass/core.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/flask_multipass/core.py b/flask_multipass/core.py index a09e62a..a52fd82 100644 --- a/flask_multipass/core.py +++ b/flask_multipass/core.py @@ -134,10 +134,13 @@ def validate_next_url(self, url): If you override this and want to allow more hosts, make sure to use a whitelist of trusted hosts to avoid creating an open redirector. """ - url_info = urlsplit(url) + # Browsers treat backslashes like forward slashes, while urllib doesn't. + # Since we just want to validate scheme and netloc here, we normalize + # slashes to those recognized by urllib. + url_info = urlsplit(url.replace('\\', '/')) if url_info.scheme and url_info.scheme not in {'http', 'https'}: return False - if url_info.scheme in {'http', 'https'} and not url_info.netloc: + if url_info.scheme and not url_info.netloc: return False return not url_info.netloc or url_info.netloc == request.host