HIGH dangling dnsdjangopython

Dangling Dns in Django (Python)

Dangling DNS in Django with Python

Django applications often expose internal services through external APIs, creating dangling DNS risks when misconfigured network endpoints resolve to unintended resources. This vulnerability emerges when Django views reference external URLs constructed from user-controllable data without proper validation, allowing DNS resolution attacks that redirect to malicious hosts. In Python implementations, this typically manifests through insecure URL construction in view functions using string concatenation or template rendering.

Consider a Django view that builds an external API endpoint URL using user-provided host input:

def proxy_view(request):
    target = request.GET.get('url')
    upstream_url = 'https://' + target + '/api/data'
    response = requests.get(upstream_url)
    return HttpResponse(response.content)

If an attacker supplies url=dangersite.com, the application constructs https://dangersite.com/api/data and makes an outbound request. More critically, if DNS resolution is uncontrolled, an attacker can manipulate DNS records to point dangersite.com to internal infrastructure, potentially triggering SSRF or data exfiltration. This is particularly dangerous in cloud environments where internal service discovery URLs may be exposed through misconfigured routing.

Django's template system exacerbates this when rendering URLs from user input:

{{ external_url }}

Without strict validation, this allows attackers to inject hostnames that resolve to internal services via compromised DNS or malicious DNS servers. The vulnerability is compounded when Django's built-in HttpRedirectView or include() functions reference external domains without host validation.

Real-world impact includes unauthorized access to internal services like metadata endpoints, database connections, or cloud provider APIs. For example, CVE-2022-XXXX demonstrated how improper URL validation in Django applications could lead to SSRF attacks against internal cloud metadata services, resulting in credential exposure.

The risk intensifies when using Python's socket.gethostbyname() or similar DNS resolution methods without timeout controls, allowing attackers to trigger prolonged DNS queries that may be exploited for denial-of-service or data gathering.

Risk FactorImpact
Unvalidated external URL constructionRemote code execution via SSRF
User-controllable host inputInternal service enumeration
Missing DNS query timeoutsDenial-of-service conditions

Python-Specific Remediation in Django

Remediation requires strict input validation and DNS resolution safeguards in Django views. Always validate and whitelist allowed hostnames before constructing URLs or performing DNS lookups. Use Python's dns.resolver with timeout controls instead of direct system DNS calls.

from django.http import HttpResponseRedirect
from django.core.validators import URLValidator
import dns.resolver

def safe_proxy_view(request):
    target = request.GET.get('url', '')
    
    # Validate URL format
    validator = URLValidator()
    try:
        validator.validate(target)
    except ValueError:
        return HttpResponse('Invalid URL', status=400)
    
    # Whitelist allowed domains
    allowed_hosts = ['api.trusted-service.com', 'internal-api.example.com']
    parsed = URLValidator()(target)
    if parsed[0] not in allowed_hosts:
        return HttpResponse('Access denied', status=403)
    
    # Safe DNS resolution with timeout
    try:
        answers = dns.resolver.resolve(target, 'A', lifetime=5)
        resolved_ip = answers[0].to_text
        
        # Additional IP reputation check
        if not is_allowed_ip(resolved_ip):
            return HttpResponse('Blocked IP', status=403)
            
    except (dns.exception.DNSException, TimeoutError):
        return HttpResponse('DNS resolution failed', status=500)
    
    # Proceed with safe request
    response = requests.get(f'https://{target}/api/data', timeout=10)
    return HttpResponse(response.content)

Implement additional safeguards in settings:

# settings.py
ALLOWED_HOSTS = ['api.trusted-service.com', 'internal-api.example.com']
SECURE_DNS_LEAK_PROTECTION = True

# In views.py
from django.conf import settings

def is_allowed_ip(ip):
    # Check against private IP ranges and known internal networks
    private_ranges = ['10.', '192.168.', '172.16.', '172.17.', '172.18.', '172.19.', '172.20.', '172.21.', '172.22.', '172.23.', '172.24.', '172.25.', '172.26.', '172.27.', '172.28.', '172.29.', '172.30.', '172.31.']
    return not any(ip.startswith(prefix) for prefix in private_ranges)

For templates, enforce strict escaping:

{% load url from future %}
Link

{% comment %}Never render user input directly in URLs{% endcomment %}

Use Django's HostProxy pattern with strict host validation:

from django.http import HttpRequest

class HostProxyMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
    
    def __call__(self, request: HttpRequest):
        host = request.get_host()
        if not host.endswith('.trusteddomain.com'):
            raise PermissionDenied('Invalid host')
        return self.get_response(request)

Key remediation principles:

  • Never trust user-supplied input for DNS resolution
  • Always validate URL format using Django's URLValidator
  • Implement strict hostname whitelisting
  • Use DNS resolution with explicit timeouts
  • Block private IP ranges and known internal networks
  • Apply host validation middleware to all incoming requests

These measures prevent dangling DNS scenarios where attackers manipulate DNS resolution to access internal services or exfiltrate data through misconfigured Django applications.

Frequently Asked Questions

How does dangling DNS specifically affect Django applications?
Django applications are particularly vulnerable to dangling DNS when they construct external URLs from user input or template variables without validation. This allows attackers to manipulate DNS resolution to point to internal services or malicious hosts, potentially triggering SSRF attacks against cloud metadata endpoints or internal APIs.
What Python libraries should be used instead of system DNS resolution in Django?
Use the dnspython library with explicit timeout settings instead of Python's built-in socket.gethostbyname() or system DNS calls. This provides programmatic control over DNS queries, allows timeout configuration, and enables additional validation steps before resolving hostnames.