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.
| Approach | Risk if misused | Recommended use |
|---|---|---|
| Bearer token present → redirect | Open redirect + token leakage | Never use |
| Validate target host against allowlist | Low if allowlist is strict | Permitted external redirects |
| Relative path redirect only | Minimal | Default 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.