Dns Cache Poisoning in Django with Bearer Tokens
Dns Cache Poisoning in Django with Bearer Tokens — how this specific combination creates or exposes the vulnerability
DNS cache poisoning (also known as DNS spoofing) occurs when an attacker corrupts the resolver cache with forged DNS responses, redirecting a victim to a malicious IP. In Django applications that rely on external service discovery or microservice communication, poisoned DNS entries can cause requests to be sent to an attacker-controlled host. When those requests include Bearer tokens, the combination can expose privileged authentication material or allow an attacker to act on behalf of the victim.
Consider a Django service that resolves an internal auth host via DNS at runtime, then forwards Authorization: Bearer
Django itself does not perform DNS caching in user code, but the underlying OS resolver and any HTTP client used (such as requests or httpx) will. Therefore, the exposure arises from how the application uses Bearer tokens in combination with external hostnames that are subject to DNS resolution. For example, if a Django backend calls an identity provider endpoint via a hostname that can be poisoned, and the request includes a Bearer token, intercepted or altered traffic may compromise the token.
Real-world attack patterns include intercepting requests to an OAuth introspection endpoint or a backend API gateway identified by a friendly hostname. If an API expects Bearer token authorization and the hostname resolves to the wrong IP due to cache poisoning, tokens can be leaked to an attacker who terminates TLS or proxies traffic. This maps to OWASP API Top 10 controls around authentication and integrity issues and can be relevant to compliance frameworks such as PCI-DSS and SOC2 when token confidentiality is required.
To detect such risks, scans should validate that services using Bearer tokens do not rely on untrusted or shared DNS resolution for critical endpoints and that transport security is enforced. middleBrick checks for insecure handling of authentication in combination with host resolution and flags scenarios where tokens could be exposed via network-layer weaknesses.
Bearer Tokens-Specific Remediation in Django — concrete code fixes
Remediation focuses on ensuring that token handling is resilient to DNS manipulation by avoiding reliance on mutable DNS records for authorization and by hardening HTTP clients. Use explicit IPs or service discovery mechanisms that are not subject to cache poisoning where possible, and enforce strict transport security. Below are concrete patterns and code examples for Django projects.
1. Use HTTPS with certificate verification and avoid hostname-based routing for token endpoints
Always verify TLS certificates and avoid falling back to HTTP. Pin or explicitly trust certificate authorities for critical endpoints.
import requests
# Good: explicit HTTPS with cert verification and a pinned fingerprint or CA
response = requests.get(
'https://auth.example.com/introspect',
headers={'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'},
verify='/path/to/ca-bundle.pem', # or True (default) with proper CA store
timeout=5,
)
2. Configure HTTP clients to use specific IPs or SRV records when DNS must be used
If you must rely on DNS, prefer SRV records and validate resolved endpoints. For high-security scenarios, consider static IPs or a service mesh with mTLS to reduce DNS dependency.
import httpx
import ssl
# Example using httpx with custom resolver settings (platform-specific)
# Prefer mTLS client certificates for additional assurance
ssl_context = ssl.create_default_context(cafile='/path/to/ca-bundle.pem')
client = httpx.Client(
headers={'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'},
verify=ssl_context,
follow_redirects=False,
)
resp = client.get('https://auth.example.com/token')
3. Validate and restrict scopes and audiences in token introspection
When using opaque tokens, introspect them via a trusted endpoint and validate audience, issuer, and scopes. Do not trust hostname-derived metadata alone.
from django.http import JsonResponse
import requests
def introspect_token(request):
token = request.headers.get('Authorization', '').replace('Bearer ', '')
resp = requests.post(
'https://auth.example.com/introspect',
data={'token': token, 'client_id': 'django-app'},
auth=('django-client', 'django-client-secret'),
verify=True,
)
if resp.status_code == 200:
data = resp.json()
if data.get('active') and data.get('scope') and 'api:read' in data['scope'].split():
return JsonResponse({'status': 'active', 'scope': data['scope']})
return JsonResponse({'error': 'invalid_token'}, status=401)
4. Use short-lived tokens and refresh rotation to limit exposure
Prefer JWTs with short expirations and implement refresh token rotation. Store refresh tokens securely and avoid logging Authorization headers.
# Example middleware to reject tokens with excessive lifetime
import jwt
from django.conf import settings
from django.http import HttpResponseForbidden
class TokenExpiryMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
auth = request.headers.get('Authorization', '')
if auth.startswith('Bearer '):
token = auth.split(' ')[1]
try:
payload = jwt.decode(token, options={'verify_signature': False})
exp = payload.get('exp')
if exp:
# reject tokens with lifetime longer than threshold
import time
if exp - payload.get('iat', exp) > 300: # 5 minutes max
return HttpResponseForbidden('Token lifetime too long')
except Exception:
pass # let downstream handle invalid tokens
return self.get_response(request)
5. Monitor and rotate credentials, and use mTLS where feasible
Rotate client secrets and API keys regularly. Use mutual TLS for service-to-service calls to reduce reliance on bearer tokens for authentication. middleBrick scans can help identify missing transport security and weak authentication configurations.