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/flask_multipass/core.py b/flask_multipass/core.py index 62f729a..a52fd82 100644 --- a/flask_multipass/core.py +++ b/flask_multipass/core.py @@ -134,9 +134,14 @@ 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 and not url_info.netloc: + return False return not url_info.netloc or url_info.netloc == request.host def process_login(self, provider=None): 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' diff --git a/tests/test_core.py b/tests/test_core.py index 12e1403..682b1b7 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), + (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):