HIGH dns rebindingdjango

Dns Rebinding in Django

How Dns Rebinding Manifests in Django

Dns Rebinding in Django applications often exploits the framework's trust in request origins and internal service discovery. The attack works by manipulating DNS TTL values to make a malicious domain resolve to a victim's internal IP address after initially resolving to an external IP. Django's default middleware stack and view patterns can inadvertently expose internal services to this attack.

A common manifestation occurs in Django's settings.py where developers configure internal API endpoints or service URLs. Consider this vulnerable pattern:

# settings.py - Vulnerable configuration
INTERNAL_API_URL = os.getenv('INTERNAL_API_URL', 'http://localhost:8000')

An attacker registers a domain that resolves to their server initially, then uses a short TTL to rebind to the victim's internal network. When Django applications make requests to what they believe is an external API, they're actually hitting internal services.

Django's HttpRequest.META handling can also be exploited. The framework trusts certain headers for internal routing:

# views.py - Vulnerable internal service access
from django.http import JsonResponse
import requests

def internal_data_view(request):
    # Attacker can manipulate X-Forwarded-For to appear as internal request
    if request.META.get('HTTP_X_FORWARDED_FOR') == '127.0.0.1':
        internal_data = requests.get('http://localhost:9000/internal-data')
        return JsonResponse({'data': internal_data.json()})
    return JsonResponse({'error': 'Unauthorized'}, status=403)

Another Django-specific vector involves the ORM's connection pooling when connecting to internal databases. If connection strings are constructed from request parameters:

# urls.py - Vulnerable URL pattern
urlpatterns = [
    path('db-query/<str:db_host>/<str:query>/', db_query_view),
]

# views.py - Vulnerable database access
import psycopg2
from django.http import JsonResponse

def db_query_view(request, db_host, query):
    conn = psycopg2.connect(
        host=db_host,  # DNS rebinding target
        database='app_data'
    )
    cur = conn.cursor()
    cur.execute(query)
    results = cur.fetchall()
    return JsonResponse({'results': results})

This pattern allows attackers to specify internal database hosts that get resolved through manipulated DNS, potentially exposing internal database services.

Django-Specific Detection

Detecting DNS rebinding in Django requires examining both configuration files and runtime behavior. Start by auditing settings.py for hardcoded internal service URLs and environment variables that accept external input:

# Check for vulnerable patterns in settings.py
grep -r 'localhost\|127\.0\.0\.1\|0\.0\.0\.0' .
grep -r 'INTERNAL_API_URL\|INTERNAL_SERVICE' .

# Look for unsafe request header usage
grep -r 'HTTP_X_FORWARDED_FOR\|HTTP_X_REAL_IP' .

middleBrick's Django-specific scanning identifies these patterns automatically. The scanner tests for DNS rebinding by:

  • Analyzing OpenAPI specs for internal service endpoints
  • Checking middleware configurations for unsafe header processing
  • Testing response behaviors when internal services are accessed
  • Verifying that no internal services are exposed through public endpoints

Run middleBrick to detect Django-specific DNS rebinding vulnerabilities:

# Scan a Django application
middlebrick scan https://your-django-app.com

# Check for specific findings related to internal service access
middlebrick report --category "SSRF" --format json

The scanner's LLM security module also checks for AI-specific rebinding attacks where model endpoints might be exposed to internal network access. This is particularly relevant for Django applications using ML libraries or AI integrations.

Manual testing involves setting up a test domain with short TTL values and monitoring Django's behavior when the domain resolves to internal IPs. Use tools like dnsrebind.sh or custom DNS servers to simulate the attack.

Django-Specific Remediation

Remediating DNS rebinding in Django requires a defense-in-depth approach. Start with configuration hardening:

# settings.py - Secure configuration
import socket
from urllib.parse import urlparse

def validate_internal_url(url):
    """Validate that URLs don't point to internal networks"""
    parsed = urlparse(url)
    if not parsed.scheme or not parsed.netloc:
        raise ValueError('Invalid URL format')
    
    # Check for private IP ranges
    try:
        ip = socket.gethostbyname(parsed.netloc)
        octets = list(map(int, ip.split('.')))
        
        # Private IP ranges: 10.x, 172.16-31.x, 192.168.x, 127.x
        if octets[0] == 10 or \
           (octets[0] == 172 and 16 <= octets[1] <= 31) or \
           (octets[0] == 192 and octets[1] == 168) or \
           octets[0] == 127:
            raise ValueError('Internal network address detected')
    except socket.gaierror:
        # DNS resolution failed, treat as potentially malicious
        raise ValueError('DNS resolution failed')
    
    return url

# Use validated URLs
INTERNAL_API_URL = validate_internal_url(
    os.getenv('INTERNAL_API_URL', 'https://trusted-external-service.com')
)

For request handling, implement strict header validation and use Django's built-in security middleware:

# middleware.py - DNS rebinding protection
from django.middleware.security import SecurityMiddleware
import ipaddress

class DNSSecurityMiddleware(SecurityMiddleware):
    def __init__(self, get_response):
        super().__init__(get_response)
        # Define trusted internal networks
        self.trusted_networks = [
            ipaddress.IPv4Network('10.0.0.0/8'),
            ipaddress.IPv4Network('172.16.0.0/12'),
            ipaddress.IPv4Network('192.168.0.0/16'),
            ipaddress.IPv4Network('127.0.0.0/8'),
        ]
    
    def __call__(self, request):
        # Check X-Forwarded-For header for internal IPs
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR', '')
        if x_forwarded_for:
            for ip_str in x_forwarded_for.split(','):
                ip = ipaddress.ip_address(ip_str.strip())
                if any(ip in network for network in self.trusted_networks):
                    # Reject requests appearing to come from internal networks
                    return JsonResponse({'error': 'Internal network access denied'}, status=403)
        
        return self.get_response(request)

# Add to settings.py
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'yourproject.middleware.DNSSecurityMiddleware',
    # ... other middleware
]

For database connections, use Django's connection validation and avoid dynamic host construction:

# utils.py - Safe database connection
from django.db import connections
from django.db.utils import OperationalError

def safe_db_query(query, db_alias='default'):
    """Execute query with DNS rebinding protection"""
    try:
        # Verify connection is to allowed host
        conn = connections[db_alias]
        conn.ensure_connection()
        
        cursor = conn.cursor()
        cursor.execute(query)
        return cursor.fetchall()
    except OperationalError as e:
        # Log and handle connection errors
        logger.error(f'Database connection failed: {e}')
        raise
    except Exception as e:
        logger.error(f'Database query failed: {e}')
        raise

Implement Django's built-in security features:

# settings.py - Security configurations
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True

# Disable internal service access through Django
INTERNAL_IPS = []  # Never allow internal IPs
ALLOWED_HOSTS = ['yourdomain.com']  # Restrict to specific domains

For production deployments, use a Web Application Firewall (WAF) in front of Django to block DNS rebinding attempts at the network level. Combine this with regular middleBrick scans to ensure new vulnerabilities aren't introduced during development.

Frequently Asked Questions

How does DNS rebinding differ from SSRF in Django applications?
DNS rebinding is a specific technique where attackers manipulate DNS TTL to make a domain resolve to internal IPs after initial resolution. SSRF (Server-Side Request Forgery) is the broader vulnerability class where servers make unauthorized requests. In Django, DNS rebinding is often the attack vector that enables SSRF against internal services. middleBrick detects both issues but reports them separately - DNS rebinding focuses on the DNS manipulation aspect, while SSRF detection looks at the request forgery patterns.
Can Django's built-in security features prevent DNS rebinding?
Django's security middleware provides some protection but isn't specifically designed for DNS rebinding. Features like SECURE_SSL_REDIRECT and SECURE_BROWSER_XSS_FILTER help with general security but don't address DNS manipulation. You need additional middleware like the DNSSecurityMiddleware shown above, combined with proper network configuration and middleBrick scanning. The key is validating all external inputs and restricting internal service access through Django's configuration.