diff --git a/doc/api/guides/custom_checkout.rst b/doc/api/guides/custom_checkout.rst index 26fb904a6..dc6c34fe4 100644 --- a/doc/api/guides/custom_checkout.rst +++ b/doc/api/guides/custom_checkout.rst @@ -94,7 +94,9 @@ If you want the user to return to your application after the payment is complete "Plugins". Enable the plugin "Redirection from order page". Then, go to the new page "Settings", then "Redirection". Enter the base URL of your web application. This will allow you to redirect to pages under this base URL later on. For example, if you want users to be redirected to ``https://example.org/order/return?tx_id=1234``, you could now -either enter ``https://example.org`` or ``https://example.org/order/``. +either enter ``https://example.org/order/`` or ``https://example.org/``. +Please note that in the latter case the trailing slash is required, ``https://example.org`` is not allowed to prevent. +Only base URLs with a secure (``https://``) or local (``http://localhost``) origin are permitted. The user will be redirected back to your page instead of pretix' order confirmation page after the payment, **regardless of whether it was successful or not**. We will append an ``error=…`` query parameter with an error diff --git a/src/pretix/plugins/returnurl/signals.py b/src/pretix/plugins/returnurl/signals.py index 6ad8c2232..894bed67b 100644 --- a/src/pretix/plugins/returnurl/signals.py +++ b/src/pretix/plugins/returnurl/signals.py @@ -71,11 +71,15 @@ def returnurl_process_request(sender, request, **kwargs): u = request.GET.get('return_url') if not sender.settings.returnurl_prefix: raise PermissionDenied('No return URL prefix set.') - elif not u.startswith(sender.settings.returnurl_prefix): + elif not check_against_prefix_list(u, sender.settings.returnurl_prefix): raise PermissionDenied('Invalid return URL.') request.session[key] = u +def check_against_prefix_list(u, allowlist): + return any(u.startswith(allow.strip()) for allow in allowlist.split("\n") if allow.strip() != "") + + @receiver(nav_event_settings, dispatch_uid='returnurl_nav') def navbar_info(sender, request, **kwargs): url = resolve(request.path_info) diff --git a/src/pretix/plugins/returnurl/views.py b/src/pretix/plugins/returnurl/views.py index 68d87d47d..437a46695 100644 --- a/src/pretix/plugins/returnurl/views.py +++ b/src/pretix/plugins/returnurl/views.py @@ -19,6 +19,8 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # +import re + from django import forms from django.urls import reverse from django.utils.translation import gettext_lazy as _ @@ -31,10 +33,14 @@ from pretix.control.views.event import ( class ReturnSettingsForm(SettingsForm): - returnurl_prefix = forms.URLField( - label=_("Base redirection URL"), - help_text=_("Redirection will only be allowed to URLs that start with this prefix."), + returnurl_prefix = forms.RegexField( + label=_("Base redirection URLs"), + help_text=_("Redirection will only be allowed to URLs that start with one of these prefixes. " + "Enter one or more allowed URL prefix per line. " + "URL prefixes must include a slash after the hostname."), required=False, + widget=forms.Textarea, + regex=re.compile(r'^((https://.*/.*|http://localhost[:/].*)\n*)*$') )