From 1f68bb5683608ebe1ce47f0c9d37dad9482d1df9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Suliga?= Date: Fri, 19 Aug 2016 14:32:21 +0200 Subject: [PATCH] Refs #26902 -- Protected against insecure redirects in set_language(). --- django/views/i18n.py | 5 +++-- docs/releases/1.11.txt | 10 +++++----- tests/view_tests/tests/test_i18n.py | 17 +++++++++++++++++ 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/django/views/i18n.py b/django/views/i18n.py index b3aaccbd33..a6bf8bcff4 100644 --- a/django/views/i18n.py +++ b/django/views/i18n.py @@ -37,11 +37,12 @@ def set_language(request): any state. """ next = request.POST.get('next', request.GET.get('next')) - if (next or not request.is_ajax()) and not is_safe_url(url=next, host=request.get_host()): + if ((next or not request.is_ajax()) and + not is_safe_url(url=next, host=request.get_host(), require_https=request.is_secure())): next = request.META.get('HTTP_REFERER') if next: next = urlunquote(next) # HTTP_REFERER may be encoded. - if not is_safe_url(url=next, host=request.get_host()): + if not is_safe_url(url=next, host=request.get_host(), require_https=request.is_secure()): next = '/' response = http.HttpResponseRedirect(next) if next else http.HttpResponse(status=204) if request.method == 'POST': diff --git a/docs/releases/1.11.txt b/docs/releases/1.11.txt index 64afe7a841..802a09e6a9 100644 --- a/docs/releases/1.11.txt +++ b/docs/releases/1.11.txt @@ -356,12 +356,12 @@ 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 ``manage.py test --liveserver`` option is removed. -Protection against insecure redirects in :mod:`django.contrib.auth` views -------------------------------------------------------------------------- +Protection against insecure redirects in :mod:`django.contrib.auth` and ``i18n`` 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. +``LoginView``, ``LogoutView`` (and the deprecated function-based equivalents), +and :func:`~django.views.i18n.set_language` protect users from being redirected +to non-HTTPS ``next`` URLs when the app is running over HTTPS. Miscellaneous ------------- diff --git a/tests/view_tests/tests/test_i18n.py b/tests/view_tests/tests/test_i18n.py index e1cf44782d..ae56990fe6 100644 --- a/tests/view_tests/tests/test_i18n.py +++ b/tests/view_tests/tests/test_i18n.py @@ -54,6 +54,23 @@ class I18NTests(TestCase): self.assertEqual(response.url, '/') self.assertEqual(self.client.session[LANGUAGE_SESSION_KEY], lang_code) + def test_setlang_http_next(self): + """ + The set_language view only redirects to the 'next' argument if it is + "safe" and its scheme is https if the request was sent over https. + """ + lang_code = self._get_inactive_language_code() + non_https_next_url = 'http://testserver/redirection/' + post_data = dict(language=lang_code, next=non_https_next_url) + # Insecure URL in POST data. + response = self.client.post('/i18n/setlang/', data=post_data, secure=True) + self.assertEqual(response.url, '/') + self.assertEqual(self.client.session[LANGUAGE_SESSION_KEY], lang_code) + # Insecure URL in HTTP referer. + response = self.client.post('/i18n/setlang/', secure=True, HTTP_REFERER=non_https_next_url) + self.assertEqual(response.url, '/') + self.assertEqual(self.client.session[LANGUAGE_SESSION_KEY], lang_code) + def test_setlang_redirect_to_referer(self): """ The set_language view redirects to the URL in the referer header when