Skip to content

Commit 71c493a

Browse files
authored
#5127 - Prevent open redirects via the OIDC next redirect parameter (sm) (#5139)
1 parent b2d150e commit 71c493a

2 files changed

Lines changed: 22 additions & 2 deletions

File tree

src/djangooidc/tests/test_views.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,3 +542,14 @@ def test_logout_callback_redirects(self, _):
542542
# ASSERTIONS
543543
self.assertEqual(response.status_code, 302)
544544
self.assertEqual(response.url, reverse("logout"))
545+
546+
def test_external_redirect_login(self, mock_client):
547+
with less_console_noise():
548+
# Testing with an invalid callback url
549+
callback_url = "http://www.google.com"
550+
mock_client.create_authn_request.side_effect = self.say_hi
551+
mock_client.get_default_acr_value.side_effect = self.create_acr
552+
response = self.client.get(reverse("login"), {"next": callback_url})
553+
session = mock_client.create_authn_request.call_args[0][0]
554+
self.assertEqual(session["next"], "/")
555+
self.assertEqual(response.status_code, 200)

src/djangooidc/views.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from django.contrib.auth import logout as auth_logout
88
from django.contrib.auth import authenticate, login
99
from django.http import HttpResponseRedirect
10+
from django.utils.http import url_has_allowed_host_and_scheme
1011
from django.shortcuts import redirect
1112
from urllib.parse import parse_qs, urlencode
1213

@@ -76,7 +77,11 @@ def openid(request):
7677
logger.debug("OIDC client is None, attempting to initialize")
7778
_initialize_client()
7879
request.session["acr_value"] = CLIENT.get_default_acr_value()
79-
request.session["next"] = request.GET.get("next", "/")
80+
if url_has_allowed_host_and_scheme(request.GET.get("next", "/"), None):
81+
request.session["next"] = request.GET.get("next", "/")
82+
else:
83+
logger.warning(f"Invalid redirect: {request.GET.get("next")}")
84+
request.session["next"] = "/"
8085
# Create the authentication request
8186
return CLIENT.create_authn_request(request.session)
8287
except Exception as err:
@@ -135,7 +140,11 @@ def login_callback(request):
135140
# In the event of a state mismatch between OP and session, redirect the user to the
136141
# beginning of login process without raising an error to the user. Attempt once.
137142
logger.warning(f"No State Defined: {nsd_err}")
138-
return redirect(request.session.get("next", "/"))
143+
if url_has_allowed_host_and_scheme(request.GET.get("next", "/"), None):
144+
return redirect(request.session.get("next", "/"))
145+
else:
146+
logger.warning(f"Invalid redirect: {request.GET.get("next")}")
147+
return redirect("/")
139148
else:
140149
# Clear the flag if the exception is not caught
141150
request.session.pop("redirect_attempted", None)

0 commit comments

Comments
 (0)