HIGH open redirectdjangobearer tokens

Open Redirect in Django with Bearer Tokens

Open Redirect in Django with Bearer Tokens — how this specific combination creates or exposes the vulnerability

An open redirect in Django involving Bearer tokens occurs when an endpoint accepts both a redirect target (e.g., a next or redirect_to parameter) and authorization via a Bearer token, and the application uses the token to decide whether to follow or allow the redirect without strict validation. Even though Bearer tokens are typically used for authentication and should not directly influence redirect behavior, a common anti-pattern is to inspect the Authorization header to determine whether to allow a redirect to a user-supplied URL. If the token is accepted and the redirect target is not strictly validated, an attacker can supply a malicious URL while presenting a valid token, causing the application to issue a 302/301 response that sends the client’s credentials or session context to an arbitrary site.

Consider a Django view that conditionally enforces HTTPS and redirects based on an Authorization header presence:

import os
from django.http import HttpResponseRedirect
from django.conf import settings

def redirect_view(request):
    next_url = request.GET.get('next', '/')
    auth = request.headers.get('Authorization', '')
    # Dangerous: using Bearer token to gate redirect decisions
    if auth.startswith('Bearer ') or settings.DEBUG:
        return HttpResponseRedirect(next_url)
    return HttpResponseRedirect('/login/')

In this pattern, an attacker can supply next=https://evil.com along with a valid Bearer token, and the server will redirect the authenticated client to the attacker’s site. If the client’s browser or a malicious script follows the redirect, sensitive Authorization headers or cookies may be leaked to the attacker. Even if the token is not used to decide the redirect, leaking the token in logs or error messages during an open redirect flow can expose it to third parties.

An OpenAPI spec that does not validate the redirect target can exacerbate the issue. For example, a path defined without constraining the next parameter allows any URI, and runtime testing may reveal that the endpoint responds with 302 when a Bearer token is provided, indicating that authorization influences redirect behavior. This violates the principle that redirects should be limited to same-origin or explicitly trusted destinations.

Real-world impact aligns with common web vulnerabilities: an open redirect can facilitate phishing and token theft. Combined with Bearer tokens, which are often long-lived, a leaked token can be reused across services. Scans that test unauthenticated attack surfaces may not catch this when tokens are required, but authenticated scans or manual testing with a valid Bearer token can expose the unsafe redirect logic.

Bearer Tokens-Specific Remediation in Django — concrete code fixes

Remediation focuses on decoupling token validation from redirect decisions and strictly constraining the redirect target. Do not use the presence or contents of a Bearer token to determine whether to redirect, and never allow user input to specify arbitrary URLs.

1) Remove token-based gating for redirects. Use a fixed list of allowed hosts and always redirect to a safe default or a validated internal path:

from django.http import HttpResponseRedirect
from django.urls import resolve, Resolver404
from django.conf import settings

ALLOWED_REDIRECT_HOSTS = {'example.com', 'app.example.com'}

def validate_redirect_url(url):
    if not url:
        return False
    from urllib.parse import urlparse
    parsed = urlparse(url)
    # Reject non-HTTP(S) schemes
    if parsed.scheme not in ('http', 'https'):
        return False
    # Enforce same-origin by default; extend with allowed hosts if needed
    try:
        resolve(parsed.path)
        return True
    except Resolver404:
        pass
    # If you must allow external same-host redirects, check netloc
    if parsed.netloc and parsed.netloc in ALLOWED_REDIRECT_HOSTS:
        return True
    return False

def safe_redirect_view(request):
    next_url = request.GET.get('next', '/')
    if validate_redirect_url(next_url):
        return HttpResponseRedirect(next_url)
    return HttpResponseRedirect('/')

2) If you must support external redirects, use django’s built-in redirect validation utilities or a strict allowlist. For SPAs, prefer relative paths or pre-registered redirect URIs stored server-side:

from django.shortcuts import redirect

def redirect_with_allowlist(request):
    next_url = request.GET.get('next', '/')
    allowed_paths = {'/dashboard/', '/profile/', '/settings/'}
    if next_url in allowed_paths:
        return redirect(next_url)
    return redirect('/')

3) Ensure Bearer tokens are not exposed in logs, error messages, or redirect URLs. Do not concatenate tokens into URLs or include them in Referer headers. Use secure, HttpOnly cookies for session tokens when possible, and keep Bearer tokens in Authorization headers only. For APIs, validate the token early but do not let it affect navigation logic:

import jwt
from django.http import JsonResponse, HttpResponseRedirect
from django.conf import settings

def api_handler(request):
    auth = request.headers.get('Authorization', '')
    if not auth.startswith('Bearer '):
        return JsonResponse({'error': 'Unauthorized'}, status=401)
    token = auth.split(' ', 1)[1]
    try:
        payload = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256'])
    except jwt.ExpiredSignatureError:
        return JsonResponse({'error': 'Token expired'}, status=401)
    except jwt.InvalidTokenError:
        return JsonResponse({'error': 'Invalid token'}, status=401)
    # Token validated; proceed without using it to decide redirect
    return JsonResponse({'status': 'ok'})

4) For OAuth2 or token-based flows, use the state parameter to preserve context instead of redirecting to user-supplied URLs. Validate the state on callback and avoid using the token itself as a redirect decision factor.

ApproachRisk if misusedRecommended use
Bearer token present → redirectOpen redirect + token leakageNever use
Validate target host against allowlistLow if allowlist is strictPermitted external redirects
Relative path redirect onlyMinimalDefault safe choice

FAQ

  • Can an open redirect leak my Bearer token? Yes, if the redirect sends your browser to an attacker-controlled site while your token is still present in headers or cookies, the site can capture the token via Referer headers, logs, or malicious JavaScript.
  • Does middleBrick detect open redirects involving Bearer tokens? middleBrick scans endpoints and maps findings to frameworks like OWASP API Top 10; authenticated scans with a valid Bearer token can surface redirect logic that should be remediated using the guidance above.

Frequently Asked Questions

How can I test for open redirects with Bearer tokens in Django during development?
Use curl or a script to send requests with an Authorization: Bearer header and a next parameter pointing to an external host. Observe the response status and Location header; ensure the server does not redirect to external hosts and that the Bearer token is not reflected in logs or error messages.
Does middleBrick’s OAuth2/Bearer token scanning require authentication?
middleBrick can scan unauthenticated attack surfaces; for endpoints that require Bearer tokens, providing a valid token during authenticated scans helps surface logic that improperly uses token context in redirect decisions.