Don't use Django's redirect() for user-supplied paths

This commit is contained in:
Raphael Michel
2022-11-17 11:46:03 +01:00
parent f18fb02d0b
commit 7b58ddbfde
5 changed files with 23 additions and 12 deletions

View File

@@ -61,6 +61,7 @@ from pretix.base.forms.auth import (
)
from pretix.base.models import TeamInvite, U2FDevice, User, WebAuthnDevice
from pretix.base.services.mail import SendMailException
from pretix.helpers.http import redirect_to_url
from pretix.helpers.webauthn import generate_challenge
logger = logging.getLogger(__name__)
@@ -81,7 +82,7 @@ def process_login(request, user, keep_logged_in):
twofa_url = reverse('control:auth.login.2fa')
if next_url and url_has_allowed_host_and_scheme(next_url, allowed_hosts=None):
twofa_url += '?next=' + quote(next_url)
return redirect(twofa_url)
return redirect_to_url(twofa_url)
else:
auth_login(request, user)
request.session['pretix_auth_login_time'] = int(time.time())
@@ -110,7 +111,7 @@ def login(request):
if request.user.is_authenticated:
next_url = backend.get_next_url(request) or 'control:index'
if next_url and url_has_allowed_host_and_scheme(next_url, allowed_hosts=None):
return redirect(next_url)
return redirect_to_url(next_url)
return redirect(reverse('control:index'))
if request.method == 'POST':
form = LoginForm(backend=backend, data=request.POST, request=request)
@@ -136,8 +137,8 @@ def logout(request):
if 'next' in request.GET and url_has_allowed_host_and_scheme(request.GET.get('next'), allowed_hosts=None):
next += '?next=' + quote(request.GET.get('next'))
if 'back' in request.GET and url_has_allowed_host_and_scheme(request.GET.get('back'), allowed_hosts=None):
return redirect(request.GET.get('back'))
return redirect(next)
return redirect_to_url(request.GET.get('back'))
return redirect_to_url(next)
def register(request):
@@ -443,7 +444,7 @@ class Login2FAView(TemplateView):
del request.session['pretix_auth_2fa_user']
del request.session['pretix_auth_2fa_time']
if "next" in request.GET and url_has_allowed_host_and_scheme(request.GET.get("next"), allowed_hosts=None):
return redirect(request.GET.get("next"))
return redirect_to_url(request.GET.get("next"))
return redirect(reverse('control:index'))
else:
messages.error(request, _('Invalid code, please try again.'))

View File

@@ -71,6 +71,7 @@ from pretix.control.permissions import (
AdministratorPermissionRequiredMixin, StaffMemberRequiredMixin,
)
from pretix.control.views.auth import get_u2f_appid
from pretix.helpers.http import redirect_to_url
from pretix.helpers.webauthn import generate_challenge, generate_ukey
REAL_DEVICE_TYPES = (TOTPDevice, WebAuthnDevice, U2FDevice)
@@ -138,7 +139,7 @@ class ReauthView(TemplateView):
request.session['pretix_auth_last_used'] = t
next_url = get_auth_backends()[request.user.auth_backend].get_next_url(request)
if next_url and url_has_allowed_host_and_scheme(next_url, allowed_hosts=None):
return redirect(next_url)
return redirect_to_url(next_url)
return redirect(reverse('control:index'))
else:
messages.error(request, _('The password you entered was invalid, please try again.'))
@@ -153,7 +154,7 @@ class ReauthView(TemplateView):
request.session['pretix_auth_login_time'] = t
request.session['pretix_auth_last_used'] = t
if next_url and url_has_allowed_host_and_scheme(next_url, allowed_hosts=None):
return redirect(next_url)
return redirect_to_url(next_url)
return redirect(reverse('control:index'))
return super().get(request, *args, **kwargs)
@@ -749,7 +750,7 @@ class StartStaffSession(StaffMemberRequiredMixin, RecentAuthenticationRequiredMi
)
if "next" in request.GET and url_has_allowed_host_and_scheme(request.GET.get("next"), allowed_hosts=None):
return redirect(request.GET.get("next"))
return redirect_to_url(request.GET.get("next"))
else:
return redirect(reverse("control:index"))

View File

@@ -20,7 +20,9 @@
# <https://www.gnu.org/licenses/>.
#
from django.conf import settings
from django.http import StreamingHttpResponse
from django.http import (
HttpResponsePermanentRedirect, HttpResponseRedirect, StreamingHttpResponse,
)
class ChunkBasedFileResponse(StreamingHttpResponse):
@@ -40,3 +42,8 @@ def get_client_ip(request):
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
return ip
def redirect_to_url(to, permanent=False):
redirect_class = HttpResponsePermanentRedirect if permanent else HttpResponseRedirect
return redirect_class(to)

View File

@@ -63,6 +63,7 @@ from pretix.base.services.cart import (
remove_cart_position,
)
from pretix.base.views.tasks import AsyncAction
from pretix.helpers.http import redirect_to_url
from pretix.multidomain.urlreverse import eventreverse
from pretix.presale.views import (
CartMixin, EventViewMixin, allow_cors_if_namespaced,
@@ -652,7 +653,7 @@ class RedeemView(NoSearchIndexViewMixin, EventViewMixin, CartMixin, TemplateView
if err:
messages.error(request, _(err))
return redirect(self.get_next_url() + "?voucher_invalid")
return redirect_to_url(self.get_next_url() + "?voucher_invalid")
return super().dispatch(request, *args, **kwargs)

View File

@@ -50,6 +50,7 @@ from pretix.base.customersso.oidc import (
from pretix.base.models import Customer, InvoiceAddress, Order, OrderPosition
from pretix.base.services.mail import mail
from pretix.base.settings import PERSON_NAME_SCHEMES
from pretix.helpers.http import redirect_to_url
from pretix.multidomain.models import KnownDomain
from pretix.multidomain.urlreverse import build_absolute_uri, eventreverse
from pretix.presale.forms.customer import (
@@ -620,7 +621,7 @@ class SSOLoginView(RedirectBackMixin, View):
})
if self.provider.method == "oidc":
return redirect(oidc_authorize_url(self.provider, f'{nonce}§{next_url}', redirect_uri))
return redirect_to_url(oidc_authorize_url(self.provider, f'{nonce}§{next_url}', redirect_uri))
else:
raise Http404("Unknown SSO method.")
@@ -814,7 +815,7 @@ class SSOLoginReturnView(RedirectBackMixin, View):
})
else:
customer_login(self.request, customer)
return redirect(self.get_success_url(redirect_to))
return redirect_to_url(self.get_success_url(redirect_to))
def _fail(self, message, popup_origin):
if not popup_origin: