Conversation
| self.logger.warning(_('Google Recaptcha não configurado!')) | ||
| messages.error(request, _('Google Recaptcha não configurado!')) | ||
| return redirect(request.META.get('HTTP_REFERER', '/')) | ||
| return redirect(request.headers.get('referer', '/')) |
Check warning
Code scanning / CodeQL
URL redirection from remote source Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 13 days ago
In general, to fix this issue you must not redirect directly to a URL taken from user input (including headers). Instead, either (a) validate that the URL is safe (e.g., same host, allowed scheme, relative path), or (b) ignore it and redirect to a fixed safe location.
Since this is a Django project, the best low‑impact fix is to use django.utils.http.url_has_allowed_host_and_scheme to validate the referer before using it. If the referer is allowed, redirect to it; otherwise, fall back to '/'. This keeps the existing “go back to where you came from” behavior when safe, but prevents redirection to arbitrary external URLs.
Concretely for sapl/base/views.py:
- Add
url_has_allowed_host_and_schemeto the imports (fromdjango.utils.http), keeping all existing imports unchanged. - In both
RecuperarSenhaEmailView.getandRecuperarSenhaEmailView.post, replaceredirect(request.headers.get('referer', '/'))with logic that:- Reads the referer header into a variable.
- Uses
url_has_allowed_host_and_scheme(referer, allowed_hosts={request.get_host()}, require_https=request.is_secure())to check safety. - If safe, redirects to
referer; otherwise, redirects to'/'.
This preserves functionality for legitimate in‑site flows and blocks external redirects.
| @@ -23,7 +23,7 @@ | ||
| from django.utils import timezone | ||
| from django.utils.decorators import method_decorator | ||
| from django.utils.encoding import force_bytes | ||
| from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode | ||
| from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode, url_has_allowed_host_and_scheme | ||
| from django.utils.translation import gettext_lazy as _ | ||
| from django.views.generic import (FormView, ListView) | ||
| from django.views.generic.base import RedirectView, TemplateView | ||
| @@ -119,7 +119,13 @@ | ||
| if not google_recaptcha_configured(): | ||
| self.logger.warning(_('Google Recaptcha não configurado!')) | ||
| messages.error(request, _('Google Recaptcha não configurado!')) | ||
| return redirect(request.headers.get('referer', '/')) | ||
| referer = request.headers.get('referer', '/') | ||
| if url_has_allowed_host_and_scheme( | ||
| referer, | ||
| allowed_hosts={request.get_host()}, | ||
| require_https=request.is_secure()): | ||
| return redirect(referer) | ||
| return redirect('/') | ||
|
|
||
| return PasswordResetView.get(self, request, *args, **kwargs) | ||
|
|
||
| @@ -128,7 +134,13 @@ | ||
| if not google_recaptcha_configured(): | ||
| self.logger.warning(_('Google Recaptcha não configurado!')) | ||
| messages.error(request, _('Google Recaptcha não configurado!')) | ||
| return redirect(request.headers.get('referer', '/')) | ||
| referer = request.headers.get('referer', '/') | ||
| if url_has_allowed_host_and_scheme( | ||
| referer, | ||
| allowed_hosts={request.get_host()}, | ||
| require_https=request.is_secure()): | ||
| return redirect(referer) | ||
| return redirect('/') | ||
|
|
||
| return PasswordResetView.post(self, request, *args, **kwargs) | ||
|
|
| self.logger.warning(_('Google Recaptcha não configurado!')) | ||
| messages.error(request, _('Google Recaptcha não configurado!')) | ||
| return redirect(request.META.get('HTTP_REFERER', '/')) | ||
| return redirect(request.headers.get('referer', '/')) |
Check warning
Code scanning / CodeQL
URL redirection from remote source Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 13 days ago
In general, to fix untrusted URL redirection, you must not feed raw user input (including headers like Referer) directly into redirect. Instead, either (1) only redirect to a set of known-safe URLs (a whitelist), or (2) validate that the target is a safe relative URL or lies on an allowed host using Django’s URL utilities.
In this specific case, the cleanest fix with minimal behavior change is: keep the idea of “go back to the referring page if possible”, but validate that the Referer is safe. Django already provides url_has_allowed_host_and_scheme for this purpose. We can import it and then, in both get and post methods of RecuperarSenhaEmailView, read referer = request.headers.get('referer', '/'), then check url_has_allowed_host_and_scheme(referer, allowed_hosts={request.get_host()}, require_https=request.is_secure()). If and only if it returns True, redirect to referer; otherwise, fall back to '/'. This preserves existing intended behavior (return to previous page within the same site) while preventing open redirects to external domains.
Concretely:
- Add an import for
url_has_allowed_host_and_schemefromdjango.utils.httpat the top ofsapl/base/views.py(expanding the existing import from that module). - In
RecuperarSenhaEmailView.get, replacereturn redirect(request.headers.get('referer', '/'))with a small block that:- fetches the raw referer,
- validates it with
url_has_allowed_host_and_scheme, - chooses a
safe_url(refererif valid,'/'otherwise), - redirects to
safe_url.
- Do the same replacement in
RecuperarSenhaEmailView.postfor the identical redirect.
No new methods or complex logic are required; we only add the import and wrap the redirect target with Django’s built-in safety check.
| @@ -23,7 +23,7 @@ | ||
| from django.utils import timezone | ||
| from django.utils.decorators import method_decorator | ||
| from django.utils.encoding import force_bytes | ||
| from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode | ||
| from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode, url_has_allowed_host_and_scheme | ||
| from django.utils.translation import gettext_lazy as _ | ||
| from django.views.generic import (FormView, ListView) | ||
| from django.views.generic.base import RedirectView, TemplateView | ||
| @@ -119,7 +119,16 @@ | ||
| if not google_recaptcha_configured(): | ||
| self.logger.warning(_('Google Recaptcha não configurado!')) | ||
| messages.error(request, _('Google Recaptcha não configurado!')) | ||
| return redirect(request.headers.get('referer', '/')) | ||
| referer = request.headers.get('referer', '/') | ||
| if url_has_allowed_host_and_scheme( | ||
| referer, | ||
| allowed_hosts={request.get_host()}, | ||
| require_https=request.is_secure() | ||
| ): | ||
| safe_url = referer | ||
| else: | ||
| safe_url = '/' | ||
| return redirect(safe_url) | ||
|
|
||
| return PasswordResetView.get(self, request, *args, **kwargs) | ||
|
|
||
| @@ -128,7 +137,16 @@ | ||
| if not google_recaptcha_configured(): | ||
| self.logger.warning(_('Google Recaptcha não configurado!')) | ||
| messages.error(request, _('Google Recaptcha não configurado!')) | ||
| return redirect(request.headers.get('referer', '/')) | ||
| referer = request.headers.get('referer', '/') | ||
| if url_has_allowed_host_and_scheme( | ||
| referer, | ||
| allowed_hosts={request.get_host()}, | ||
| require_https=request.is_secure() | ||
| ): | ||
| safe_url = referer | ||
| else: | ||
| safe_url = '/' | ||
| return redirect(safe_url) | ||
|
|
||
| return PasswordResetView.post(self, request, *args, **kwargs) | ||
|
|
| self.logger.warning(_('Google Recaptcha não configurado!')) | ||
| messages.error(request, _('Google Recaptcha não configurado!')) | ||
| return redirect(request.META.get('HTTP_REFERER', '/')) | ||
| return redirect(request.headers.get('referer', '/')) |
Check warning
Code scanning / CodeQL
URL redirection from remote source Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 13 days ago
In general, to fix untrusted URL redirection you must avoid using raw user-controlled data (query parameters, headers like Referer, etc.) directly as redirect targets. Either (1) ignore user input and redirect to a fixed internal URL, (2) maintain a whitelist of allowed redirect paths, or (3) validate the supplied URL with a function such as Django’s url_has_allowed_host_and_scheme and fall back to a safe default if validation fails.
For this specific case in AcompanhamentoDocumentoView.get, the intent is clearly to send the user back to where they came from if Recaptcha is not configured, falling back to / (home page). The simplest safe fix that preserves this behavior is:
- Read the referer header.
- Validate it with
url_has_allowed_host_and_scheme, restricting to the current host (or Django’sALLOWED_HOSTS). - If valid, redirect to it; otherwise, redirect to
'/'.
This avoids open redirects while still letting legitimate same-origin referers work. Concretely:
- Add an import for
url_has_allowed_host_and_schemefromdjango.utils.httpat the top ofsapl/protocoloadm/views.py. - Replace the
return redirect(request.headers.get('referer', '/'))on line 281 with logic that:- Extracts
referer = request.headers.get('referer') - Checks
if referer and url_has_allowed_host_and_scheme(referer, allowed_hosts={request.get_host()}, require_https=request.is_secure()): - Redirects to
refererif valid, else to'/'.
- Extracts
No new methods or classes are required; just this small conditional and the import.
| @@ -29,6 +29,7 @@ | ||
| from django.views.generic.base import RedirectView, TemplateView | ||
| from django.views.generic.edit import FormView | ||
| from django_filters.views import FilterView | ||
| from django.utils.http import url_has_allowed_host_and_scheme | ||
|
|
||
| import sapl | ||
| from sapl.base.email_utils import do_envia_email_confirmacao | ||
| @@ -278,7 +279,13 @@ | ||
| if not google_recaptcha_configured(): | ||
| self.logger.warning(_('Google Recaptcha não configurado!')) | ||
| messages.error(request, _('Google Recaptcha não configurado!')) | ||
| return redirect(request.headers.get('referer', '/')) | ||
| referer = request.headers.get('referer') | ||
| if referer and url_has_allowed_host_and_scheme( | ||
| referer, | ||
| allowed_hosts={request.get_host()}, | ||
| require_https=request.is_secure()): | ||
| return redirect(referer) | ||
| return redirect('/') | ||
|
|
||
| pk = self.kwargs['pk'] | ||
| documento = DocumentoAdministrativo.objects.get(id=pk) |
| self.logger.warning(_('Google Recaptcha não configurado!')) | ||
| messages.error(request, _('Google Recaptcha não configurado!')) | ||
| return redirect(request.META.get('HTTP_REFERER', '/')) | ||
| return redirect(request.headers.get('referer', '/')) |
Check warning
Code scanning / CodeQL
URL redirection from remote source Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 13 days ago
In general, to fix open redirects you must not feed unvalidated user-controlled data into redirect(). Instead, restrict redirects to either: (a) a fixed internal URL, (b) a choice from a server-side whitelist, or (c) only relative/host-allowed URLs validated with something like Django’s url_has_allowed_host_and_scheme.
The minimal change here is to stop redirecting to an arbitrary Referer header and instead perform a safe redirect. Since the failure case is “Google Recaptcha não configurado!”, the simplest safe behavior is to ignore the Referer and send the user to a known safe page (e.g., /, as already used in the other early returns). This preserves functionality (error message + redirect to home) and removes the tainted data flow.
Concretely:
- In
AcompanhamentoDocumentoView.get, changereturn redirect(request.headers.get('referer', '/'))toreturn redirect('/'). - In
AcompanhamentoDocumentoView.post, changereturn redirect(request.headers.get('referer', '/'))toreturn redirect('/').
No new imports or helpers are needed; we already import redirect from django.shortcuts.
| @@ -278,7 +278,7 @@ | ||
| if not google_recaptcha_configured(): | ||
| self.logger.warning(_('Google Recaptcha não configurado!')) | ||
| messages.error(request, _('Google Recaptcha não configurado!')) | ||
| return redirect(request.headers.get('referer', '/')) | ||
| return redirect('/') | ||
|
|
||
| pk = self.kwargs['pk'] | ||
| documento = DocumentoAdministrativo.objects.get(id=pk) | ||
| @@ -297,7 +297,7 @@ | ||
| if not google_recaptcha_configured(): | ||
| self.logger.warning(_('Google Recaptcha não configurado!')) | ||
| messages.error(request, _('Google Recaptcha não configurado!')) | ||
| return redirect(request.headers.get('referer', '/')) | ||
| return redirect('/') | ||
|
|
||
| form = AcompanhamentoDocumentoForm(request.POST) | ||
| pk = self.kwargs['pk'] |
c1fcbe7 to
b0dc138
Compare
Descrição
Issue Relacionada
Motivação e Contexto
Como Isso Foi Testado?
Capturas de Tela (se apropriado):
Tipos de Mudanças
Checklist: