Dangling Dns in Django with Hmac Signatures
Dangling Dns in Django with Hmac Signatures — how this specific combination creates or exposes the vulnerability
A dangling DNS reference in a Django application using HMAC signatures can occur when a hostname or domain is dynamically resolved at runtime and then used as part of a signed value without strict validation. For example, if a view constructs a URL or token using a user-supplied subdomain that resolves through DNS, and then signs that URL with an HMAC key, an attacker may be able to redirect the signed payload to a host they control. Because the signature is computed over the unresolved or partially resolved input, the server may accept the signed data at face value after DNS resolution changes, effectively trusting a dangling or attacker-owned endpoint.
Consider a scenario where Django uses HMAC to sign a confirmation link that includes a tenant subdomain. If the application resolves the subdomain to an internal or external host and embeds the resolved hostname into the signed payload, an attacker who can register or influence the DNS record for that subdomain can change where the signed link points. The signature remains valid because the server recomputes the HMAC over the same serialized data, but the underlying resource has moved or been replaced. This can facilitate phishing, session fixation, or unauthorized data access when the signed value is used for authentication or authorization decisions.
The interaction with HMAC signatures is subtle: HMAC ensures integrity and authenticity of the signed data, but it does not guarantee that the data’s semantic meaning — especially hostnames and URLs — remain fixed or safe. If the application trusts DNS to map a key used in signature verification, and that mapping can be altered, the security boundary shifts from cryptographic integrity to reliance on external, mutable infrastructure. This is particularly risky in multi-tenant setups where subdomains are dynamically assigned and DNS records may be managed by different teams or third parties.
In practice, this can manifest as a business logic flaw where a signed redirect or token includes a hostname that later resolves to an unintended service. For instance, a signed URL containing https://tenant.example.com/action might be constructed by resolving tenant via DNS at request time. If an attacker points tenant.example.com to a malicious server, the signed URL still validates, but the client is directed to the attacker’s endpoint. Because the signature checks pass, Django may treat the request as legitimate, leading to insecure redirects or data exposure.
To detect such issues, scans like those provided by middleBrick evaluate whether signed values incorporate hostnames or other DNS-dependent identifiers, and whether runtime resolution diverges from the assumptions made at signing time. The tool checks for inconsistent handling of references between OpenAPI specifications and runtime behavior, highlighting cases where DNS-based indirection is not tightly constrained. This helps surface configurations where dangling DNS references can undermine HMAC-based integrity guarantees.
Hmac Signatures-Specific Remediation in Django — concrete code fixes
To mitigate dangling DNS risks when using HMAC signatures in Django, ensure that any data used to construct or verify signed values is canonical, immutable, and independent from runtime DNS resolution. Prefer storing or encoding identifiers rather than hostnames, and validate references against a strict allowlist or configuration.
When you must include a hostname, resolve it once at configuration time and pin it, rather than resolving it per request. Below are concrete code examples demonstrating secure handling of HMAC signatures in Django.
Example 1: Signing a canonical URL without runtime DNS resolution
import hmac
import hashlib
import base64
from django.conf import settings
from django.utils.http import urlencode
def build_signed_url(user_id, tenant_slug):
# Use a static or pre-validated base; do not resolve tenant_slug via DNS at call time
base_url = f"https://app.example.com/{tenant_slug}/action"
payload = urlencode({
'user_id': user_id,
'tenant': tenant_slug,
})
message = f"{base_url}?{payload}".encode('utf-8')
key = settings.SECRET_KEY.encode('utf-8')
signature = base64.urlsafe_b64encode(
hmac.new(key, message, hashlib.sha256).digest()
).rstrip(b'=').decode('utf-8')
return f"{base_url}?{payload}&sig={signature}"
def verify_signed_url(path, received_sig):
# Recompute signature over the received path without additional DNS lookups
key = settings.SECRET_KEY.encode('utf-8')
message = path.encode('utf-8')
expected = base64.urlsafe_b64encode(
hmac.new(key, message, hashlib.sha256).digest()
).rstrip(b'=').decode('utf-8')
return hmac.compare_digest(expected, received_sig)
Example 2: Using Django’s built-in signing utilities with strict data
from django.core.signing import TimestampSigner, BadSignature, SignatureExpired
signer = TimestampSigner(key=None) # uses Django's SECRET_KEY
def create_tenant_token(user_id, tenant_id):
# Sign a structured payload that does not include a hostname
value = f"{user_id}:{tenant_id}"
return signer.sign(value)
def verify_tenant_token(signed_value):
try:
original = signer.unsign(signed_value, max_age=3600)
user_id, tenant_id = original.split(':')
# Perform additional authorization checks here
return {'user_id': user_id, 'tenant_id': tenant_id}
except (BadSignature, SignatureExpired) as e:
# Handle invalid or expired tokens securely
return None
Example 3: Validating hostnames against an allowlist
ALLOWED_TENANT_HOSTS = {
'tenant-a': 'https://tenant-a.example.com',
'tenant-b': 'https://tenant-b.example.com',
}
def resolve_and_validate(tenant_slug, requested_host):
canonical = ALLOWED_TENANT_HOSTS.get(tenant_slug)
if not canonical:
return None
# Ensure the requested host matches the canonical host exactly
if requested_host != canonical:
return None
return canonical
In addition to code changes, apply operational practices such as restricting DNS management for signing-related domains, using short-lived tokens, and monitoring for unexpected redirect targets. middleBrick’s scans can surface areas where hostnames are included in signed payloads or where DNS mappings are referenced inconsistently, helping teams prioritize remediation.
For teams using the full middleBrick platform, the Dashboard can track security scores over time, the CLI can integrate scans into scripts, and the GitHub Action can enforce security gates in CI/CD. The MCP Server enables these checks directly from AI coding assistants, supporting secure development workflows without requiring manual configuration.