def get_safe_login_redirect_url(request): from sso.oauth2.models import allowed_hosts redirect_to = get_request_param(request, REDIRECT_FIELD_NAME, '') # Ensure the user-originating redirection url is safe. # allow external hosts, for redirect after password_create_complete if url_has_allowed_host_and_scheme(redirect_to, allowed_hosts=allowed_hosts()): return redirect_to else: return resolve_url(settings.LOGIN_REDIRECT_URL)
def get_redirect_url(self): """Return the user-originating redirect URL if it's safe.""" redirect_to = self.request.POST.get( self.redirect_field_name, self.request.GET.get(self.redirect_field_name, '')) url_is_safe = url_has_allowed_host_and_scheme( url=redirect_to, allowed_hosts=self.get_success_url_allowed_hosts(), require_https=self.request.is_secure(), ) return redirect_to if url_is_safe else ''
def dispatch(self, request, *args, **kwargs): next = request.GET.get('next') if next: url_is_safe = url_has_allowed_host_and_scheme( url=next, allowed_hosts=self.get_success_url_allowed_hosts(), require_https=self.request.is_secure(), ) if url_is_safe: request.session['next'] = next request.session['next_expiration'] = datetime.timestamp(datetime.now() + timedelta(minutes=15)) return super().dispatch(request, *args, **kwargs)
def safelink_callback(attrs, new=False): """ Makes sure that all links to a different domain are passed through a redirection handler to ensure there's no passing of referers with secrets inside them. """ url = attrs.get((None, 'href'), '/') if not url_has_allowed_host_and_scheme(url, allowed_hosts=None) and not url.startswith('mailto:') and not url.startswith('tel:'): signer = signing.Signer(salt='safe-redirect') attrs[None, 'href'] = reverse('redirect') + '?url=' + urllib.parse.quote(signer.sign(url)) attrs[None, 'target'] = '_blank' attrs[None, 'rel'] = 'noopener' return attrs
def get_success_url(self): from django.utils.http import url_has_allowed_host_and_scheme # Copied from django.contrib.auth.views.LoginView.get_success_url redirect_to = self.get_redirect_to(use_default=True) url_is_safe = url_has_allowed_host_and_scheme( url=redirect_to, allowed_hosts={self.request.get_host()}, require_https=self.request.is_secure(), ) if not url_is_safe: return resolve_url(settings.LOGIN_REDIRECT_URL) return redirect_to
def dispatch(self, request, *args, **kwargs): request.user.is_administrator = request.user.is_superuser request.user.is_superuser = False request.user.save(update_fields=["is_administrator", "is_superuser"]) messages.success( request, _("You are now an administrator instead of a superuser.") ) params = request.GET.copy() url = urllib.parse.unquote(params.pop("next", [""])[0]) if url and url_has_allowed_host_and_scheme(url, allowed_hosts=None): return redirect(url + ("?" + params.urlencode() if params else "")) return redirect(reverse("orga:event.list"))
def test_secure_param_https_urls(self): secure_urls = ( 'https://example.com/p', 'HTTPS://example.com/p', '/view/?param=http://example.com', ) for url in secure_urls: with self.subTest(url=url): self.assertIs( url_has_allowed_host_and_scheme(url, allowed_hosts={'example.com'}, require_https=True), True, )
def test_secure_param_non_https_urls(self): insecure_urls = ( 'http://example.com/p', 'ftp://example.com/p', '//example.com/p', ) for url in insecure_urls: with self.subTest(url=url): self.assertIs( url_has_allowed_host_and_scheme(url, allowed_hosts={'example.com'}, require_https=True), False, )
def get(self, request, *args, **kwargs): backend = get_auth_backends()[request.user.auth_backend] u = backend.request_authenticate(request) if u and u == request.user: next_url = backend.get_next_url(request) t = int(time.time()) 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(reverse('control:index')) return super().get(request, *args, **kwargs)
def get_redirect_url(self): """ Return the URL to redirect to when canceling is successful. Looks in query string for ?next, ensuring it is on the same domain. """ next = self.request.GET.get(REDIRECT_FIELD_NAME) # url_has_allowed_host_and_scheme() will ensure we don't redirect to another domain if next and url_has_allowed_host_and_scheme(next, allowed_hosts=settings.ALLOWED_HOSTS): return next else: return self.redirect_url
def safelink_callback(attrs, new=False): url = attrs.get((None, 'href'), '/') if not url_has_allowed_host_and_scheme( url, allowed_hosts=None) and not url.startswith( 'mailto:') and not url.startswith('tel:'): signer = signing.Signer(salt='safe-redirect') attrs[None, 'href'] = reverse('redirect') + '?url=' + urllib.parse.quote( signer.sign(url)) attrs[None, 'target'] = '_blank' attrs[None, 'rel'] = 'noopener' return attrs
def post(self, request, *args, **kwargs): r = request.POST.get("webauthn", "") valid = False if 'webauthn_challenge' in self.request.session and r.startswith('{'): challenge = self.request.session['webauthn_challenge'] resp = json.loads(r) try: devices = [WebAuthnDevice.objects.get(user=self.request.user, credential_id=resp.get("id"))] except WebAuthnDevice.DoesNotExist: devices = U2FDevice.objects.filter(user=self.request.user) for d in devices: try: wu = d.webauthnuser if isinstance(d, U2FDevice): # RP_ID needs to be appId for U2F devices, but we can't # set it that way in U2FDevice.webauthnuser, since that # breaks the frontend part. wu.rp_id = settings.SITE_URL webauthn_assertion_response = webauthn.WebAuthnAssertionResponse( wu, resp, challenge, settings.SITE_URL, uv_required=False # User Verification ) sign_count = webauthn_assertion_response.verify() except Exception: logger.exception('U2F login failed') else: if isinstance(d, WebAuthnDevice): d.sign_count = sign_count d.save() valid = True break valid = valid or self.form.is_valid() if valid: t = int(time.time()) request.session['pretix_auth_login_time'] = t 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(reverse('control:index')) else: messages.error(request, _('The password you entered was invalid, please try again.')) return self.get(request, *args, **kwargs)
def get_redirect_to_url(self): redirect_to_url = self.request.POST.get( self.redirect_field_name, self.request.GET.get(self.redirect_field_name, '') ) url_is_safe = url_has_allowed_host_and_scheme( url=redirect_to_url, allowed_hosts=self.get_success_url_allowed_hosts(), require_https=self.request.is_secure(), ) if url_is_safe: self.success_url = redirect_to_url
def _do_next(request, response): """See DoNextModelAdmin""" if "next" in request.GET: if not url_has_allowed_host_and_scheme( request.GET["next"], allowed_hosts={request.get_host()}): raise DisallowedRedirect if "_save" in request.POST: return HttpResponseRedirect(request.GET["next"]) if response is not None: return HttpResponseRedirect("{}?{}".format( response.url, request.GET.urlencode())) return response
def post(self, request): """Carries out the login, redirects to get if it fails""" # redirect target on successful login next_page = request.POST.get("next", "") # redirect target on failed login login_page = "{page}?next={next_page}".format(page=reverse("login"), next_page=next_page) username = request.POST.get("user", None) if not username: messages.error(request, _("Username missing")) return HttpResponseRedirect(login_page) password = request.POST.get("pwd", None) if not password: messages.error(request, _("Password missing")) return HttpResponseRedirect(login_page) # find the user from the configured login systems, and verify pwd user = authenticate(username=username, password=password) if not user: messages.error(request, _("Wrong username or password.")) return HttpResponseRedirect(login_page) if not user.is_active: send_activation_email(user, request) messages.error( request, _("Please activate your account first. " "We have just re-sent your activation email"), ) return HttpResponseRedirect(login_page) # set up the user's session login(request, user) if next_page: domain = RequestSite(request).domain allowed_hosts = [domain] if url_has_allowed_host_and_scheme(next_page, allowed_hosts): return HttpResponseRedirect(next_page) else: # TODO: log a warning that next_page is not # considered a safe redirect target pass return HttpResponseRedirect(DEFAULT_LOGIN_REDIRECT)
def get_success_url(self): """Get success url from redirect field.""" redirect_to = self.request.POST.get( self.redirect_field_name, self.request.GET.get(self.redirect_field_name, '/')) url_is_safe = url_has_allowed_host_and_scheme( url=redirect_to, allowed_hosts=(self.request.get_host(), ), require_https=self.request.is_secure(), ) return redirect_to if url_is_safe else '/'
def set_semester(request: WSGIRequest) -> HttpResponse: redirect_url: Optional[str] = request.POST.get("next") or request.GET.get( "next") if not url_has_allowed_host_and_scheme(url=redirect_url, allowed_hosts=request.get_host(), require_https=True): redirect_url = request.META.get("HTTP_REFERER") if not url_has_allowed_host_and_scheme( url=redirect_url, allowed_hosts=request.get_host(), require_https=True): redirect_url = "/" # should not happen :) if request.method == "POST": semester_pk = int(request.POST.get("semester") or -1) # semester is always present try: Semester.objects.get(pk=semester_pk) except Semester.DoesNotExist: pass else: request.session[SEMESTER_SESSION_KEY] = semester_pk return HttpResponseRedirect(redirect_url or "/")
def get_redirect_url(self): """Return URL to redirect the logged in user if the URL is safe.""" redirect_to = self.request.POST.get( # get redirect url from the hidden next field self.redirect_field_name, self.request.GET.get(self.redirect_field_name, '')) url_is_safe = url_has_allowed_host_and_scheme( url=redirect_to, allowed_hosts=self.get_success_url_allowed_hosts(), require_https=self.request.is_secure(), ) return redirect_to if url_is_safe else ''
def signin(r): try: import urlparse as _urlparse from urllib import unquote except Exception: import urllib.parse as _urlparse from urllib.parse import unquote next_url = r.GET.get('next', _default_next_url()) try: if 'next=' in unquote(next_url): next_url = _urlparse.parse_qs( _urlparse.urlparse(unquote(next_url)).query)['next'][0] except Exception: next_url = r.GET.get('next', _default_next_url()) # Only permit signin requests where the next_url is a safe URL if parse_version(get_version()) >= parse_version('2.0'): url_ok = url_has_allowed_host_and_scheme(next_url, None) else: url_ok = url_has_allowed_host_and_scheme(next_url) if not url_ok: return HttpResponseRedirect(get_reverse([denied, 'denied', 'django_saml2_auth:denied'])) r.session['login_next_url'] = next_url saml_client = _get_saml_client(get_current_domain(r)) _, info = saml_client.prepare_for_authenticate() redirect_url = None for key, value in info['headers']: if key == 'Location': redirect_url = value break return HttpResponseRedirect(redirect_url)
def redirect_to_referer(self, request): """ Redirects the user back to where they come from, unless it is unsafe. """ referer = request.META.get('HTTP_REFERER', '') referer_is_safe = url_has_allowed_host_and_scheme( url=referer, allowed_hosts={request.get_host()}, require_https=request.is_secure()) if referer_is_safe: next_url = resolve_url(referer) else: next_url = reverse('admin:index', current_app=self.name) return redirect(next_url)
def get_success_url(self): messages.success(self.request, "Thank you for your feedback!") if ( self.source_redirect and url_has_allowed_host_and_scheme( self.object.source_url, allowed_hosts=None ) and "report_problem" not in self.object.source_url ): return self.object.source_url else: return "/"
def get_redirect_url(self): """ Return the user-originating redirect URL if it's safe. Mugged from Django's LoginView down a dark alley. """ redirect_to = self.request.POST.get('next', self.request.GET.get('next', '')) url_is_safe = url_has_allowed_host_and_scheme( url=redirect_to, allowed_hosts={self.request.get_host()}, require_https=self.request.is_secure(), ) return redirect_to if url_is_safe else ''
def post(self, request): form = LoginForm(request, data=request.POST) if form.is_valid(): # Check where should the user be redirected next_redirect = request.POST.get("next", "") if not url_has_allowed_host_and_scheme( url=next_redirect, allowed_hosts=[request.get_host()]): next_redirect = reverse("home") auth_login(request, form.get_user()) messages.info(request, "Logged in as {}.".format(request.user)) return HttpResponseRedirect(next_redirect) return render(request, self.template, {"form": form})
def get_success_url(request, redirect_field_name="next", default="/") -> str: """ Return the success URL identified by the given redirect field name in the request's POST or GET parameters. If it is not provided or is unsafe, return the given default URL. """ # This is mostly cribbed from django.contrib.auth.views.LoginView. redirect_to = request.POST.get(redirect_field_name, request.GET.get(redirect_field_name, "")) url_is_safe = url_has_allowed_host_and_scheme( url=redirect_to, allowed_hosts={request.get_host()}, require_https=request.is_secure(), ) return redirect_to if url_is_safe else default
def post(self, request, *args, **kwargs): if self._target == self.object.state: messages.info( request, _("Somebody else was faster than you: this submission was already in the state you wanted to change it to." ), ) elif self.is_allowed: self.do() else: self.do(force=True) url = self.request.GET.get("next") if url and url_has_allowed_host_and_scheme(url, allowed_hosts=None): return redirect(url) return redirect(self.object.orga_urls.base)
def get_safe_url(request, param_name=None, fallback_url=None, url=None): url = url or request.GET.get(param_name) or request.POST.get(param_name) allowed_hosts = settings.ALLOWED_HOSTS require_https = request.is_secure() if url: if settings.DEBUG: # In DEBUG mode the network location part `127.0.0.1:8000` contains # a port and fails the validation of `url_has_allowed_host_and_scheme` # since it's not a member of `allowed_hosts`: # https://github.com/django/django/blob/525274f/django/utils/http.py#L413 # As a quick fix, we build a new URL without the port. from urllib.parse import ParseResult, urlparse url_info = urlparse(url) url_without_port = ParseResult( scheme=url_info.scheme, netloc=url_info.hostname, path=url_info.path, params=url_info.params, query=url_info.query, fragment=url_info.fragment, ).geturl() if url_has_allowed_host_and_scheme(url_without_port, allowed_hosts, require_https): return url else: if url_has_allowed_host_and_scheme(url, allowed_hosts, require_https): return url return fallback_url
def get_next_url(self, request): """ Get the next url from the querystring parameters (?next=/my/next/page). If the next parameter is not there, returns the default redirect url """ next_url = request.GET.get("next") if not next_url: next_url = reverse(magicauth_settings.LOGGED_IN_REDIRECT_URL_NAME) if not url_has_allowed_host_and_scheme( next_url, allowed_hosts={request.get_host() }, require_https=True): # We are not logging the unsafe URL to prevent code injections in logs logger.warning( "[MagicAuth] an unsafe URL was used through a login link") raise Http404 return next_url
def is_safe_url(url, request, allowed_hosts=None): """Use Django's `url_has_allowed_host_and_scheme()` and pass a configured list of allowed hosts and enforce HTTPS. `allowed_hosts` can be specified.""" if not allowed_hosts: allowed_hosts = ( settings.DOMAIN, urlparse(settings.CODE_MANAGER_URL).netloc, ) if settings.ADDONS_FRONTEND_PROXY_PORT: allowed_hosts = allowed_hosts + ( f'{settings.DOMAIN}:{settings.ADDONS_FRONTEND_PROXY_PORT}', ) require_https = request.is_secure() if request else False return url_has_allowed_host_and_scheme(url, allowed_hosts=allowed_hosts, require_https=require_https)
def login(self, request, extra_context=None): """ Redirects to the site login page for the given HttpRequest. """ redirect_to = request.POST.get( REDIRECT_FIELD_NAME, request.GET.get(REDIRECT_FIELD_NAME, '')) url_is_allowed = url_has_allowed_host_and_scheme( url=redirect_to, allowed_hosts={request.get_host()}, require_https=request.is_secure(), ) if not redirect_to or not url_is_allowed: redirect_to = resolve_url(settings.LOGIN_REDIRECT_URL) return redirect_to_login(redirect_to)
def _make_return_url(request, next_url): """ Make the return URL based on whether a next_url is present in the url. If there is a next_url, verify that the url is safe and allowed before using it. If not, default to the host. """ if next_url: if url_has_allowed_host_and_scheme(next_url, ALLOWED_HOSTS, require_https=True): return UOWS_LOGIN_URL + request.build_absolute_uri(next_url) else: # the next_url was not safe so don't use it - build from request.path to ignore GET parameters return UOWS_LOGIN_URL + request.build_absolute_uri(request.path) else: return UOWS_LOGIN_URL + request.build_absolute_uri()