HIGH dns rebindingdjangobearer tokens

Dns Rebinding in Django with Bearer Tokens

Dns Rebinding in Django with Bearer Tokens — how this specific combination creates or exposes the vulnerability

DNS rebinding is a network-based attack where an attacker manipulates DNS responses to make a victim’s browser believe a malicious domain resolves to an internal IP address, such as 127.0.0.1 or a backend service within the same network. When this scenario intersects with Django applications that rely on Bearer Tokens for authentication, the risk profile changes in a way that bypasses intended network boundaries.

Consider a Django API that protects endpoints with Bearer Token validation, expecting requests only from authenticated clients or trusted origins. An attacker can host a page that forces the victim’s browser to make requests to the Django server. Because the browser enforces same-origin policies based on the domain and not on the underlying network location, a rebind can cause the browser to send requests to internal services, including the Django app, using credentials the browser holds—such as cookies or, in token-based flows, an Authorization header containing a Bearer Token.

In this combination, the Bearer Token is treated by Django as a valid credential. If the Django view does not enforce strict source validation beyond token presence, the request may be processed with elevated trust. For example, an endpoint that returns sensitive internal data or triggers administrative actions may execute because the token is valid, even though the request originated from an unintended network context. This is especially dangerous when the Django application is behind a firewall or not directly exposed to the internet, as the rebinding bypasses network-layer protections by leveraging the victim’s browser as a proxy.

The attack flow typically involves an attacker-controlled page that uses JavaScript to periodically resolve a domain to 127.0.0.1 or another internal IP. If the victim has a valid Bearer Token—perhaps from a previous authenticated session stored in memory or an Authorization header set by a client-side script—the Django application cannot distinguish this from a legitimate internal request. The token is transmitted over HTTP, and if it is intercepted or reused in a different network context, the security boundary collapses.

Django’s default behavior does not inherently prevent this, because it relies on developers to enforce proper source checks and to treat Bearer Tokens as opaque credentials that must be validated in combination with other signals. Without additional safeguards, an API that only checks for a valid Bearer Token is vulnerable to DNS rebinding, as the attack exploits the trust placed in the token rather than the origin of the request.

Bearer Tokens-Specific Remediation in Django — concrete code fixes

To mitigate DNS rebinding risks in Django when using Bearer Tokens, you must implement layered controls that validate both the token and the request origin. Below are concrete remediation techniques with code examples.

1. Validate Origin and Referer Headers

Explicitly check the Origin and Referer headers against a whitelist of allowed domains. This prevents requests from unexpected network contexts, including those induced by DNS rebinding.

import re
from django.http import JsonResponse
from django.views.decorators.http import require_http_methods

ALLOWED_ORIGINS = [
    'https://your-frontend.com',
    'https://app.yourcompany.com',
]

@require_http_methods(["GET", "POST"])
def api_protected_view(request):
    origin = request.META.get('HTTP_ORIGIN')
    referer = request.META.get('HTTP_REFERER')
    if origin not in ALLOWED_ORIGINS and not (referer and referer.startswith(tuple(ALLOWED_ORIGINS))):
        return JsonResponse({'error': 'Invalid origin'}, status=403)

    auth_header = request.META.get('HTTP_AUTHORIZATION', '')
    if not auth_header.startswith('Bearer '):
        return JsonResponse({'error': 'Missing token'}, status=401)
    token = auth_header.split(' ')[1]
    if not validate_bearer_token(token):
        return JsonResponse({'error': 'Invalid token'}, status=401)

    return JsonResponse({'data': 'secure'})

def validate_bearer_token(token: str) -> bool:
    # Replace with your token validation logic, e.g., jwt.decode
    return token == 'your-secure-token'

2. Use CSRF Protection and Secure Cookies

Even with Bearer Tokens, enabling Django’s CSRF protection adds a layer of defense against cross-origin requests. For token-based flows, ensure tokens are not stored in cookies vulnerable to CSRF, and prefer sending Bearer Tokens in headers only.

from django.views.decorators.csrf import csrf_protect

@csrf_protect
def api_with_csrf(request):
    # Your token validation as above
    pass

3. Enforce SameSite and Secure Cookie Attributes

If you store session tokens in cookies, set SameSite to Strict or Lax and Secure to prevent them from being sent in cross-site contexts, reducing the impact of rebinding attacks.

# settings.py
SESSION_COOKIE_SAMESITE = 'Strict'
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SAMESITE = 'Strict'
CSRF_COOKIE_SECURE = True

4. Implement IP-Based Restrictions for Internal Services

For internal Django endpoints, restrict access to known IP ranges or loopback addresses at the middleware or firewall level. This ensures that even if a token is leaked via rebinding, the request cannot originate from unexpected network locations.

class IPAddressMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        if request.path.startswith('/internal/'):
            client_ip = request.META.get('REMOTE_ADDR')
            if not client_ip.startswith('10.') and client_ip != '127.0.0.1':
                from django.http import JsonResponse
                return JsonResponse({'error': 'Forbidden IP'}, status=403)
        response = self.get_response(request)
        return response

5. Combine with Scope-Limited Tokens

Use short-lived, scope-limited Bearer Tokens that are invalidated after use or time expiration. This reduces the window of opportunity for an attacker to exploit a leaked token following a successful DNS rebinding.

# Example using PyJWT
import jwt
import time

def generate_scoped_token(user_id: str) -> str:
    payload = {
        'user_id': user_id,
        'exp': int(time.time()) + 300,  # 5 minutes
        'scope': 'api_read',
    }
    return jwt.encode(payload, 'your-secret', algorithm='HS256')

Frequently Asked Questions

Can DNS rebinding bypass CORS protections in Django?
Yes, DNS rebinding can bypass CORS if the Django API relies solely on token validation and does not enforce strict Origin/Referer checks, because the browser may send requests to internal IPs with valid headers.
Does using HTTPS alone prevent DNS rebinding attacks in Django with Bearer Tokens?
No, HTTPS alone does not prevent DNS rebinding. You must implement origin validation, SameSite cookie attributes, and network-level restrictions to mitigate the risk effectively.