Dns Rebinding in Fastapi with Basic Auth
Dns Rebinding in Fastapi with Basic Auth — how this specific combination creates or exposes the vulnerability
DNS rebinding is an application-layer attack where the perceived identity of a host changes after the initial DNS resolution. This becomes particularly relevant when Fastapi applications rely on Basic Authentication and make outbound HTTP requests to a hostname provided or influenced by the client. Because Fastapi does not inherently validate that a resolved IP address still belongs to the original hostname, an attacker can serve a short Time-To-Live (TTL) DNS record for a domain they control, then pivot the resolution to a malicious server on a subsequent request. If the Fastapi service uses Basic Auth credentials supplied or derived from the client to authorize or forward requests, the combination can allow the attacker to bypass intended origin restrictions.
Consider a Fastapi service that accepts a hostname and port from a request, then forwards traffic using HTTP Basic Auth derived from user input or configuration. An attacker can provide a hostname that initially resolves to a benign IP but later rebinds to an internal service, such as 127.0.0.1 or a cloud metadata endpoint. Because the outbound request uses Basic Auth headers supplied by the client, the Fastapi service may treat the rebounded destination as trusted, forwarding credentials and potentially exposing internal endpoints. This pattern is relevant to the SSRF checks performed by middleBrick, and it intersects with Input Validation and Property Authorization categories in the scan’s 12 parallel checks. middleBrick’s runtime testing compares spec definitions against observed behavior, so if your OpenAPI spec describes allowed hosts or authentication schemes, deviations during rebinding scenarios are surfaced as findings.
In practice, this risk is not about Fastapi itself being vulnerable to rebinding, but about how the application uses hostnames and credentials in request workflows. Basic Auth headers remain static across retries unless explicitly changed, which can cause credentials intended for an external service to be reused against a malicious one. The scanner tests unauthenticated attack surfaces, so it can simulate scenarios where hostname parameters are manipulated while Basic Auth is present. Findings often highlight missing hostname pinning, lack of IP validation, or overly permissive CORS and proxy settings that allow rebinding to affect authenticated flows. By correlating spec definitions with runtime behavior, middleBrick can identify whether your configuration exposes authenticated endpoints to rebinding-style manipulation.
Basic Auth-Specific Remediation in Fastapi — concrete code fixes
To reduce DNS rebinding risks when using Basic Authentication in Fastapi, enforce strict hostname validation, avoid forwarding credentials to user-provided URLs, and isolate authenticated flows from dynamic target resolution. Below are concrete code examples that demonstrate secure patterns.
1. Validate hostname against an allowlist
Do not forward requests to arbitrary hostnames. Instead, resolve the hostname once and compare the IP against a strict allowlist or verify it matches an expected origin. This prevents rebinding after the initial resolution.
from fastapi import Fastapi, HTTPException, Depends
from pydantic import BaseModel
import httpx
app = Fastapi()
class TargetRequest(BaseModel):
hostname: str
port: int
path: str
ALLOWED_HOSTS = {"api.example.com": ["192.0.2.1", "198.51.100.1"]}
async def get_basic_auth_token(username: str, password: str) -> str:
import base64
token = base64.b64encode(f"{username}:{password}".encode()).decode()
return f"Basic {token}"
@app.post("/fetch")
async def fetch_resource(req: TargetRequest, auth_token: str = Depends(get_basic_auth_token)):
if req.hostname not in ALLOWED_HOSTS:
raise HTTPException(status_code=400, detail="Hostname not allowed")
resolved = httpx.get(f"https://{req.hostname}:{req.port}/resolve")
# In real usage, use DNS validation or a service registry
if resolved.status_code != 200:
raise HTTPException(status_code=502, detail="Unable to resolve hostname")
# Ensure the resolved IP is in the allowlist for the hostname
# This is simplified; use DNS checks or connection verification in production
headers = {"Authorization": auth_token}
async with httpx.AsyncClient() as client:
r = await client.get(f"http://{req.hostname}:{req.port}{req.path}", headers=headers, timeout=5.0)
return {"status": r.status_code, "data": r.text[:200]}
2. Avoid using client-supplied credentials for outbound requests
Do not propagate Basic Auth credentials received from the client to arbitrary outbound endpoints. Use a service account with least privilege or a token exchange mechanism instead.
from fastapi import Fastapi, HTTPException, Depends
from fastapi.security import HTTPBasic, HTTPBasicCredentials
import httpx
app = Fastapi()
security = HTTPBasic()
# Internal service credentials stored securely, not derived from client input
INTERNAL_AUTH = "InternalServiceToken"
async def get_internal_auth() -> str:
return f"Basic {INTERNAL_AUTH}"
@app.get("/proxy")
async def proxy_request(
url: str,
credentials: HTTPBasicCredentials = Depends(security)
):
# Validate the incoming credentials as needed
if credentials.username != "valid_user" or credentials.password != "valid_pass":
raise HTTPException(status_code=401, detail="Invalid credentials")
# Do not forward credentials to the target; use internal auth
headers = {"Authorization": await get_internal_auth()}
async with httpx.AsyncClient() as client:
try:
r = await client.get(url, headers=headers, timeout=5.0)
return {"status": r.status_code, "body": r.text[:500]}
except httpx.RequestError:
raise HTTPException(status_code=502, detail="Request failed")
3. Use low TTL and DNS rebinding protections
While Fastapi does not provide built-in DNS rebinding defenses, you can mitigate risk by setting low TTLs on your own DNS records and by validating IP ranges for internal services. For outbound calls, prefer explicit host allowlists over dynamic resolution when possible.