HIGH dangling dnsflaskbearer tokens

Dangling Dns in Flask with Bearer Tokens

Dangling Dns in Flask with Bearer Tokens — how this specific combination creates or exposes the vulnerability

A dangling DNS record occurs when a hostname (e.g., metrics.internal.example.com) previously pointed to a service but now resolves to an unpredictable address, such as a different customer’s cloud endpoint or an unmanaged IP. In Flask, if your application uses a hostname with an outdated DNS record for a backend service and you authenticate requests with Bearer Tokens, the combination can expose token leakage or authorization bypass risks.

When Flask makes outbound HTTP calls using libraries like requests, it performs DNS resolution at request time. If the hostname in your configuration resolves to an attacker-controlled host, any Bearer Token that your code attaches via Authorization: Bearer <token> can be sent to that unintended endpoint. Because the request appears to originate from your service, the external host may trust the token and return sensitive data or allow unintended actions. This is especially dangerous when the token is scoped to internal services and is not confined by network-level isolation due to the dangling record.

Consider an example where a service discovery record is not cleaned up after decommissioning an internal API. The Flask app uses a static hostname in its configuration and includes the Bearer Token in every request header. If the DNS entry now points to a public host, the token is effectively leaked to an external party. The risk is compounded if the Flask app does not validate the hostname’s ownership after resolution or does not pin certificates, enabling man-in-the-middle scenarios where the token is intercepted or the response is spoofed.

An OpenAPI-driven scanner like middleBrick can detect such issues by correlating runtime behavior with specification definitions. During a scan, it may flag unresolved or ambiguous hostnames in outbound call patterns and highlight the presence of Bearer Token usage in request headers. This helps identify whether a dangling DNS record could route authenticated traffic to unexpected endpoints. The scanner does not fix the configuration, but it provides findings with severity ratings and remediation guidance, enabling you to validate DNS hygiene and token handling in your Flask deployment.

In the context of LLM security, if your Flask service exposes an endpoint that returns model outputs, a dangling DNS record could route requests to a malicious host that injects crafted responses. Combined with Bearer Token usage, this may lead to unauthorized data access or exfiltration if the service trusts external DNS resolutions implicitly. middleBrick’s LLM/AI Security checks can surface related risks by testing for system prompt leakage and output anomalies, ensuring that token-bearing responses are not inadvertently exposed.

Bearer Tokens-Specific Remediation in Flask — concrete code fixes

To mitigate risks related to dangling DNS records when using Bearer Tokens in Flask, apply defensive coding practices that validate destinations, avoid implicit trust in DNS, and handle tokens securely. Below are concrete, realistic examples that demonstrate how to implement safer outbound HTTP calls and token handling.

1. Use explicit IP or verified hostname with certificate pinning

Instead of relying on a dynamic hostname, prefer a direct IP with SNI verification, or pin the certificate fingerprint for the expected endpoint. This reduces the impact of a dangling DNS record.

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
import ssl

session = requests.Session()
retry = Retry(total=3, backoff_factor=0.5, status_forcelist=[502, 503, 504])
adapter = HTTPAdapter(max_retries=retry)
session.mount('https://', adapter)

# Pin certificate fingerprint (SHA-256) for the expected endpoint
def make_authenticated_call(url, token):
    headers = {'Authorization': f'Bearer {token}'}
    # Use a verified destination, avoid ambiguous hostnames
    resp = session.get(url, headers=headers, timeout=5, verify='/path/to/ca-bundle.pem')
    resp.raise_for_status()
    return resp.json()

# Example usage with an explicit, controlled hostname or IP
try:
    data = make_authenticated_call('https://api.vendor.com/v1/resource', 's3cr3t_t0k3n_abc123')
except requests.exceptions.RequestException as e:
    print(f'Request failed: {e}')

2. Validate hostname against an allowlist

Before making a request, resolve the hostname and ensure it matches an expected value. This prevents accidental routing to a dangling or malicious host.

import socket
import requests

def validate_hostname(expected_host, url):
    parsed = requests.utils.urlparse(url)
    if parsed.hostname != expected_host:
        raise ValueError(f'Hostname mismatch: expected {expected_host}, got {parsed.hostname}')
    # Optional: perform additional DNS validation (e.g., IP family checks)
    try:
        resolved = socket.getaddrinfo(parsed.hostname, parsed.port or 443, socket.AF_INET)
        # Add checks as needed, e.g., ensure IP belongs to allowed ranges
    except socket.gaierror:
        raise RuntimeError('Failed to resolve hostname')

url = 'https://api.vendor.com/v1/resource'
validate_hostname('api.vendor.com', url)
headers = {'Authorization': 'Bearer s3cr3t_t0k3n_abc123'}
resp = requests.get(url, headers=headers, timeout=5)
resp.raise_for_status()

3. Secure token storage and usage

Avoid hardcoding Bearer Tokens in configuration or code. Use environment variables with strict access controls and ensure tokens are not logged. Rotate tokens regularly and scope them to least privilege.

import os
import requests

# Fetch token securely at runtime (e.g., from a secrets manager)
token = os.environ.get('API_BEARER_TOKEN')
if not token:
    raise RuntimeError('Missing Bearer Token in environment')

headers = {'Authorization': f'Bearer {token}'}
# Ensure the target URL is explicitly defined and not derived from untrusted input
target = os.environ.get('API_ENDPOINT')
if not target.startswith('https://api.vendor.com'):
    raise ValueError('Invalid API endpoint')

resp = requests.get(target, headers=headers, timeout=5)
if resp.status_code == 401:
    print('Unauthorized: check token validity and scope')
else:
    resp.raise_for_status()

4. Combine with middleware or hooks for outbound requests

In Flask, you can centralize validation logic using request hooks or a custom client wrapper to enforce DNS and token policies consistently across all outbound calls.

from flask import Flask, request
import requests

app = Flask(__name__)

def authorized_session():
    token = os.environ.get('OUTBOUND_BEARER_TOKEN')
    if not token:
        raise RuntimeError('Token missing')
    session = requests.Session()
    session.headers.update({'Authorization': f'Bearer {token}'})
    return session

@app.route('/proxy')
def proxy():
    target = os.environ.get('ALLOWED_BACKEND')
    session = authorized_session()
    resp = session.get(target, timeout=5)
    resp.raise_for_status()
    return resp.json()

These practices reduce the impact of a dangling DNS record by ensuring that token-bearing requests are routed only to verified endpoints and that tokens are handled with minimal exposure.

Frequently Asked Questions

How does middleBrick detect risks related to Bearer Tokens and DNS issues?
middleBrick performs black-box scanning and correlates runtime behavior with OpenAPI definitions. It flags ambiguous hostnames and Bearer Token usage in headers, highlighting potential routing and leakage risks without accessing internal engine details.
Can middleBrick fix dangling DNS or token misconfigurations automatically?
middleBrick detects and reports findings with severity and remediation guidance. It does not fix, patch, block, or remediate; you must apply the provided guidance to update configurations and code.