Refs #26902 -- Protected against insecure redirects in Login/LogoutView.
This commit is contained in:
parent
5e5a17028f
commit
549b90fab3
|
@ -84,7 +84,12 @@ class LoginView(FormView):
|
||||||
self.redirect_field_name,
|
self.redirect_field_name,
|
||||||
self.request.GET.get(self.redirect_field_name, '')
|
self.request.GET.get(self.redirect_field_name, '')
|
||||||
)
|
)
|
||||||
if not is_safe_url(url=redirect_to, host=self.request.get_host()):
|
url_is_safe = is_safe_url(
|
||||||
|
url=redirect_to,
|
||||||
|
host=self.request.get_host(),
|
||||||
|
require_https=self.request.is_secure(),
|
||||||
|
)
|
||||||
|
if not url_is_safe:
|
||||||
return resolve_url(settings.LOGIN_REDIRECT_URL)
|
return resolve_url(settings.LOGIN_REDIRECT_URL)
|
||||||
return redirect_to
|
return redirect_to
|
||||||
|
|
||||||
|
@ -150,8 +155,13 @@ class LogoutView(TemplateView):
|
||||||
self.redirect_field_name,
|
self.redirect_field_name,
|
||||||
self.request.GET.get(self.redirect_field_name)
|
self.request.GET.get(self.redirect_field_name)
|
||||||
)
|
)
|
||||||
|
url_is_safe = is_safe_url(
|
||||||
|
url=next_page,
|
||||||
|
host=self.request.get_host(),
|
||||||
|
require_https=self.request.is_secure(),
|
||||||
|
)
|
||||||
# Security check -- don't allow redirection to a different host.
|
# Security check -- don't allow redirection to a different host.
|
||||||
if not is_safe_url(url=next_page, host=self.request.get_host()):
|
if not url_is_safe:
|
||||||
next_page = self.request.path
|
next_page = self.request.path
|
||||||
return next_page
|
return next_page
|
||||||
|
|
||||||
|
|
|
@ -356,6 +356,13 @@ to assign a free port. The ``DJANGO_LIVE_TEST_SERVER_ADDRESS`` environment
|
||||||
variable is no longer used, and as it's also no longer used, the
|
variable is no longer used, and as it's also no longer used, the
|
||||||
``manage.py test --liveserver`` option is removed.
|
``manage.py test --liveserver`` option is removed.
|
||||||
|
|
||||||
|
Protection against insecure redirects in :mod:`django.contrib.auth` views
|
||||||
|
-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
``LoginView`` and ``LogoutView`` (and the deprecated function-based equivalents)
|
||||||
|
protect users from being redirected to non-HTTPS ``next`` URLs when the app
|
||||||
|
is running over HTTPS.
|
||||||
|
|
||||||
Miscellaneous
|
Miscellaneous
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|
|
@ -551,6 +551,23 @@ class LoginTest(AuthViewsTestCase):
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
self.assertIn(good_url, response.url, "%s should be allowed" % good_url)
|
self.assertIn(good_url, response.url, "%s should be allowed" % good_url)
|
||||||
|
|
||||||
|
def test_security_check_https(self):
|
||||||
|
login_url = reverse('login')
|
||||||
|
non_https_next_url = 'http://testserver/path'
|
||||||
|
not_secured_url = '%(url)s?%(next)s=%(next_url)s' % {
|
||||||
|
'url': login_url,
|
||||||
|
'next': REDIRECT_FIELD_NAME,
|
||||||
|
'next_url': urlquote(non_https_next_url),
|
||||||
|
}
|
||||||
|
post_data = {
|
||||||
|
'username': 'testclient',
|
||||||
|
'password': 'password',
|
||||||
|
}
|
||||||
|
response = self.client.post(not_secured_url, post_data, secure=True)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertNotEqual(response.url, non_https_next_url)
|
||||||
|
self.assertEqual(response.url, settings.LOGIN_REDIRECT_URL)
|
||||||
|
|
||||||
def test_login_form_contains_request(self):
|
def test_login_form_contains_request(self):
|
||||||
# 15198
|
# 15198
|
||||||
self.client.post('/custom_requestauth_login/', {
|
self.client.post('/custom_requestauth_login/', {
|
||||||
|
@ -919,6 +936,21 @@ class LogoutTest(AuthViewsTestCase):
|
||||||
self.assertIn(good_url, response.url, "%s should be allowed" % good_url)
|
self.assertIn(good_url, response.url, "%s should be allowed" % good_url)
|
||||||
self.confirm_logged_out()
|
self.confirm_logged_out()
|
||||||
|
|
||||||
|
def test_security_check_https(self):
|
||||||
|
logout_url = reverse('logout')
|
||||||
|
non_https_next_url = 'http://testserver/'
|
||||||
|
url = '%(url)s?%(next)s=%(next_url)s' % {
|
||||||
|
'url': logout_url,
|
||||||
|
'next': REDIRECT_FIELD_NAME,
|
||||||
|
'next_url': urlquote(non_https_next_url),
|
||||||
|
}
|
||||||
|
self.login()
|
||||||
|
response = self.client.get(url, secure=True)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertNotEqual(response.url, non_https_next_url)
|
||||||
|
self.assertEqual(response.url, logout_url)
|
||||||
|
self.confirm_logged_out()
|
||||||
|
|
||||||
def test_logout_preserve_language(self):
|
def test_logout_preserve_language(self):
|
||||||
"""Check that language stored in session is preserved after logout"""
|
"""Check that language stored in session is preserved after logout"""
|
||||||
# Create a new session with language
|
# Create a new session with language
|
||||||
|
|
Loading…
Reference in New Issue