Uncontrolled external site redirect - Host Header Injection - Python
Need
Prevention of unauthorized external site redirects
Context
- Usage of Python 3 for writing and executing Python code
- Usage of Django for building web applications in Python
Description
Insecure Code Example
from django.http import HttpResponseRedirect
from django.shortcuts import render
def redirect(request):
next_page = request.META.get('HTTP_HOST')
if next_page:
return HttpResponseRedirect(next_page)
return render(request, 'base.html')
The above Python code represents a Django view function that redirects to a URL specified by the 'HTTP_HOST' header field in the HTTP request. This code is vulnerable to Host Header Injection attacks. The main issue lies in this line: ```python next_page = request.META.get('HTTP_HOST') ``` Here, the code is getting the host header from the incoming HTTP request. A malicious user can manipulate the 'HTTP_HOST' header field in the HTTP request to any URL of their choice. The subsequent line: ```python return HttpResponseRedirect(next_page) ``` Redirects the user to the URL specified in the 'HTTP_HOST' header field. This means that a malicious user can cause the application to redirect users to any external site, which can be used for phishing attacks, spreading malware, or other malicious activities. This is known as an uncontrolled external site redirect vulnerability, or a Host Header Injection vulnerability.
Steps
- Validate the 'next_page' variable to ensure it only contains relative paths and not absolute URLs.
- Do not use the 'HTTP_HOST' header to determine the redirect location as it can be manipulated by an attacker.
- Use a whitelist of allowed hosts and only redirect to these hosts.
- Consider using Django's built-in 'is_safe_url()' function to check if the URL is safe before redirecting.
Secure Code Example
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.utils.http import is_safe_url
ALLOWED_HOSTS = ['www.example.com', 'example.com'] # Add your allowed hosts here
def redirect(request):
next_page = request.GET.get('next', '/')
url_is_safe = is_safe_url(
url=next_page,
allowed_hosts=ALLOWED_HOSTS,
require_https=request.is_secure(),
)
if url_is_safe:
return HttpResponseRedirect(next_page)
return render(request, 'base.html')
The updated code now uses Django's built-in `is_safe_url()` function to validate the `next_page` variable. This function checks if the URL is safe for redirection, i.e., it is either a relative URL or an absolute URL that matches one of the allowed hosts. The `allowed_hosts` parameter of `is_safe_url()` is set to a list of allowed hosts. This is a whitelist approach where only the hosts specified in this list are considered safe for redirection. You should replace the `ALLOWED_HOSTS` list with your own list of allowed hosts. The `require_https` parameter of `is_safe_url()` is set to `request.is_secure()`. This means that if the request is made over HTTPS, then only HTTPS URLs are considered safe. The `next_page` variable is now obtained from `request.GET.get('next', '/')` instead of `request.META.get('HTTP_HOST')`. This means that the next page is now specified as a GET parameter instead of being taken from the Host header. This prevents Host header injection attacks as the Host header is no longer used to determine the redirect location. If the `next_page` URL is safe, the client is redirected to this URL. Otherwise, the client is redirected to the base page.
References
Last updated
2023/09/18