From 05c1573e38d2b2cdcb47f5599416075e3fff1009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Mon, 25 May 2020 17:32:52 +0200 Subject: [PATCH] saml2: only allow local URLs as redirections (#43279) --- tests/test_saml_auth.py | 14 ++++++++++++-- wcs/qommon/saml2.py | 3 +++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/tests/test_saml_auth.py b/tests/test_saml_auth.py index 793d49964..673b09d33 100644 --- a/tests/test_saml_auth.py +++ b/tests/test_saml_auth.py @@ -18,6 +18,7 @@ from wcs.qommon.misc import get_lasso_server from wcs.qommon.saml2 import Saml2Directory from wcs.qommon.ident.idp import MethodAdminDirectory, AdminIDPDir from wcs.qommon import sessions, x509utils +from wcs.qommon.errors import RequestError from wcs.roles import Role from utilities import get_app, create_temporary_pub, clean_temporary_pub @@ -303,12 +304,21 @@ def test_assertion_consumer_redirect_after_url(pub): def test_assertion_consumer_full_url_redirect_after_url(pub): req = get_assertion_consumer_request(pub) - req.form['RelayState'] = 'http://example.org/foobar/?test=ok' + req.form['RelayState'] = 'http://example.net/foobar/?test=ok' saml2 = Saml2Directory() saml_response_body = req.form['SAMLResponse'] body = saml2.assertionConsumerPost() assert req.response.status_code == 303 - assert req.response.headers['location'] == 'http://example.org/foobar/?test=ok' + assert req.response.headers['location'] == 'http://example.net/foobar/?test=ok' + + +def test_assertion_consumer_external_url_redirect_after_url(pub): + req = get_assertion_consumer_request(pub) + req.form['RelayState'] = 'http://example.org/foobar/?test=ok' + saml2 = Saml2Directory() + saml_response_body = req.form['SAMLResponse'] + with pytest.raises(RequestError): + body = saml2.assertionConsumerPost() def test_saml_login_page(pub): diff --git a/wcs/qommon/saml2.py b/wcs/qommon/saml2.py index 23255cb21..a453fbd24 100644 --- a/wcs/qommon/saml2.py +++ b/wcs/qommon/saml2.py @@ -355,6 +355,9 @@ class Saml2Directory(Directory): netloc = parsed_url.netloc or request.get_server() after_url = urlparse.urlunsplit((scheme, netloc, parsed_url.path, parsed_url.query, parsed_url.fragment)) + if not (after_url.startswith(get_publisher().get_backoffice_url()) or + after_url.startswith(get_publisher().get_frontoffice_url())): + raise errors.RequestError() else: after_url = get_publisher().get_frontoffice_url() response.set_status(303)