Uncontrolled external site redirect - Python

Uncontrolled external site redirect - Python

Need

Controlled and secure external site redirects

Context

  • Usage of Python 3 for scripting and application development
  • Usage of Django for building web applications

Description

Non compliant code

        from django.http import HttpResponseRedirect
from django.shortcuts import render

def external_redirect(request):
    redirect_url = request.GET.get('next', '')
    return HttpResponseRedirect(redirect_url)
        
        

The above code is a Django view function named external_redirect. This function takes a request object as an argument and fetches the 'next' parameter from the GET request. This 'next' parameter is intended to be the URL to which the application should redirect the user.

The vulnerability here lies in the fact that there is no validation or control over the 'next' parameter. This means that an attacker can manipulate the 'next' parameter to redirect the user to any external site. This type of vulnerability is known as an uncontrolled external site redirect.

An attacker can exploit this vulnerability to launch phishing attacks by redirecting users to a malicious site that looks identical to the intended site. Once the user is on the attacker's site, any information they enter (like usernames, passwords, etc.) can be captured by the attacker. This can lead to serious security breaches, including identity theft and unauthorized access to sensitive data.

Steps

  • Validate the redirect URL: Before redirecting, check if the URL is a valid address and belongs to a trusted domain. This can be done by maintaining a whitelist of trusted domains and comparing the redirect URL against this list.
  • Use a safe redirect method: Instead of directly using the URL from the request, use a method that ensures the URL is safe before redirecting. Django provides the 'is_safe_url' method for this purpose.
  • Avoid using user input for redirect URLs: If possible, avoid using user input to determine the redirect URL. Instead, use server-side logic to determine where to redirect the user.
  • Implement proper error handling: If the redirect URL is not valid or safe, handle this error appropriately. This could involve redirecting to a default page or showing an error message to the user.

Compliant code

        from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.utils.http import is_safe_url
from django.urls import reverse

def external_redirect(request):
    redirect_url = request.GET.get('next', '')
    if not is_safe_url(url=redirect_url, allowed_hosts=request.get_host()):
        redirect_url = reverse('default_page')
    return HttpResponseRedirect(redirect_url)
        
        

The updated code now includes a check to ensure that the redirect URL is safe before proceeding with the redirection. This is done using Django's is_safe_url method, which checks if the provided URL is a safe address to redirect to.

The is_safe_url method takes two parameters: the URL to check and a list of allowed hosts. The URL is considered safe if it is either a relative URL (i.e., it doesn't specify a host) or if the host is included in the list of allowed hosts. In this case, we're using request.get_host() to get the host of the current request, which ensures that the redirect URL must belong to the same domain as the current request.

If the is_safe_url check fails (i.e., the URL is not safe), we're redirecting to a default page using Django's reverse function. This function takes a view name and returns the URL for that view, which is a safe way to handle redirects without relying on user input.

This way, even if an attacker tries to manipulate the redirect URL, they will only be redirected to the default page, preventing any potential security issues.

References