Open Redirect in Django with Hmac Signatures
Open Redirect in Django with Hmac Signatures — how this specific combination creates or exposes the vulnerability
An open redirect occurs when an application redirects a user to an arbitrary URL without proper validation. In Django, combining open redirect risks with Hmac Signatures can inadvertently expose a redirect if the signature is computed over insufficient or attacker-controlled data. A common pattern is to sign a redirect target (e.g., a next parameter) so the server can verify integrity when the user is sent back. If the signature only covers a subset of the redirect context, or if the verification logic is permissive, an attacker can supply a malicious next URL while keeping the signature valid for a benign base URL.
Consider a Django view that signs the next parameter with a shared secret using Hmac. If the signature is generated only over the next URL path but the validation accepts a different next URL during the redirect, an attacker can provide a malicious next value that passes signature verification for a trusted referrer but then redirects to an external site. This often happens when the signature is computed over a normalized or default next URL and the application reuses the signature while substituting user input at runtime. The vulnerability is not in Hmac itself, but in how the signed value is constructed, stored, and later used for redirection.
Real-world examples include signing query parameters like next or return_to without binding the signature to the full intended destination. If the view trusts additional user-supplied headers or query parameters to determine the final redirect location, the signature can be bypassed. Attack patterns such as CVE-2021-35042-style logic confusion can emerge when the same signed token is used across multiple redirect flows with different base URLs. Because the signature remains valid, the server may follow an attacker-controlled URL, leading to phishing or session manipulation.
To detect this with middleBrick, you can submit the endpoint URL and observe findings related to Authentication, BOLA/IDOR, and Property Authorization. The scanner checks whether signed parameters are used in redirect flows and whether runtime behavior deviates from the spec-defined expectations, including cross-references in OpenAPI/Swagger 2.0, 3.0, or 3.1 documents with full $ref resolution.
Hmac Signatures-Specific Remediation in Django — concrete code fixes
Remediation focuses on ensuring the signed value fully and exclusively represents the intended redirect target, and that validation binds the signature to that exact value. Use Django’s django.core.signing or hmac with a strong secret and SHA256, and always sign the exact data you will validate. Avoid including mutable or attacker-influenced fields in the signature scope unless they are tightly constrained.
Below are two concrete, working Django examples that demonstrate secure handling of Hmac-signed redirects.
Example 1: Signing the full next URL and validating it before redirect
import hmac
import hashlib
import base64
from django.http import HttpResponseRedirect, HttpRequest, HttpResponseBadRequest
from django.conf import settings
def make_signed_redirect(next_url: str, secret: bytes) -> str:
signature = hmac.new(secret, next_url.encode('utf-8'), hashlib.sha256).digest()
token = base64.urlsafe_b64encode(signature).decode('utf-8').rstrip('=')
return f'{next_url}?sig={token}'
def verify_and_redirect(request: HttpRequest, secret: bytes) -> HttpResponseRedirect | HttpResponseBadRequest:
next_url = request.GET.get('next', '')
sig = request.GET.get('sig', '')
if not next_url or not sig:
return HttpResponseBadRequest('Missing next or sig')
expected_signature = base64.urlsafe_b64encode(
hmac.new(secret, next_url.encode('utf-8'), hashlib.sha256).digest()
).decode('utf-8').rstrip('=')
if not hmac.compare_digest(expected_signature, sig):
return HttpResponseBadRequest('Invalid signature')
# Ensure next_url is relative and within allowed domains if needed
return HttpResponseRedirect(next_url)
In this example, the signature covers the entire next URL. Verification recomputes the Hmac over the received next URL and compares it with the provided signature using hmac.compare_digest to avoid timing attacks. The redirect uses the same next URL that was signed, preventing substitution.
Example 2: Using Django’s signing utilities with JSON payload
import json
from django.core import signing
from django.http import HttpResponseRedirect, HttpRequest, HttpResponseBadRequest
def make_signed_redirect_django(next_url: str, secret_key: str) -> str:
payload = json.dumps({'next': next_url})
return signing.dumps(payload, key=secret_key, salt='redirect-v1')
def verify_and_redirect_django(request: HttpRequest, secret_key: str) -> HttpResponseRedirect | HttpResponseBadRequest:
signed = request.GET.get('data', '')
try:
payload = signing.loads(signed, key=secret_key, salt='redirect-v1', max_age=300)
except signing.BadSignature:
return HttpResponseBadRequest('Invalid signature')
data = json.loads(payload)
next_url = data.get('next', '')
if not next_url:
return HttpResponseBadRequest('Missing next')
# Add additional validation, e.g., allowed_hosts check
return HttpResponseRedirect(next_url)
This second approach uses Django’s signer to serialize and verify a structured payload, making it easier to extend with additional fields (e.g., timestamp or user ID) while keeping the signature tightly bound to the intended redirect. By validating the signature before using the next URL and avoiding any late substitutions, you eliminate the mismatch that enables open redirects.
middleBrick scans can validate these patterns by checking the endpoint’s authentication and property authorization logic against the spec. The tool cross-references OpenAPI/Swagger definitions and runtime behavior to highlight whether signed parameters are used consistently and whether per-category findings indicate redirect integrity issues.