Dns Cache Poisoning in Fastapi with Jwt Tokens
Dns Cache Poisoning in Fastapi with Jwt Tokens — how this specific combination creates or exposes the vulnerability
DNS cache poisoning is a network layer attack where a resolver is tricked into accepting malicious DNS responses, causing a domain to resolve to an attacker-controlled IP. In FastAPI applications that rely on external service discovery or token introspection via hostnames, this can redirect traffic to a malicious endpoint. When JWT tokens are used for authentication, the impact is compounded because a poisoned DNS entry can lead to tokens being sent to an attacker, or attacker-controlled services being trusted as identity providers.
Consider a FastAPI service that validates JWT access tokens by fetching JSON Web Key Sets (JWKS) from a discovery URL such as https://auth.example.com/.well-known/jwks.json. If the hostname auth.example.com is subject to DNS cache poisoning, the application may resolve to an IP under attacker control. The attacker can then present a malicious JWKS, causing FastAPI to accept forged JWTs signed by a key the attacker controls. Because JWT validation typically involves signature verification using public keys obtained via DNS-resolvable endpoints, this creates a clear path to bypass authentication entirely.
Additionally, if the FastAPI application uses hostnames for outbound API calls—such as calling an identity provider or a token revocation service—DNS cache poisoning can redirect those calls. When JWT tokens carry sensitive scopes or claims, redirecting token validation or revocation checks to an attacker can enable privilege escalation or token replay. The vulnerability is not in JWT itself, but in the dependency on potentially compromised network naming. Attack patterns like this align with SSRF-like risks when combined with internal service discovery, and they can be surfaced by checks such as SSRF and External Entity Injection in security scans.
In practice, this risk is heightened when services use unvalidated or loosely validated hostnames for critical endpoints, and when JWT validation is performed without ensuring endpoint integrity. middleBrick scanning can surface SSRF and related issues in the API surface, and the LLM/AI Security checks specifically probe for system prompt leakage or unsafe consumption patterns that might amplify the impact of a poisoned DNS context.
Jwt Tokens-Specific Remediation in Fastapi — concrete code fixes
To mitigate DNS cache poisoning risks when using JWT tokens in FastAPI, ensure that all external endpoints used for key discovery and token validation are protected via strict hostname verification, pinned certificates, and, where possible, static IP resolution. Below are concrete code examples demonstrating secure JWT validation practices.
1. Validate issuer and audience, and pin JWKS hostnames
Always validate the iss (issuer) and aud (audience) claims, and avoid relying on dynamic discovery from untrusted or unverified sources. If discovery is necessary, pin the hostname to a known IP or use a hardcoded JWKS when feasible.
from fastapi import FastAPI, Depends, HTTPException, status
from jose import jwt, JWTError
from pydantic import BaseModel
import httpx
import ssl
app = FastAPI()
# Pin the JWKS host and use strict SSL context
JWKS_URL = "https://auth.example.com/.well-known/jwks.json"
PINNED_FINGERPRINT = "SHA256:abc123..." # certificate or key fingerprint
class TokenData(BaseModel):
sub: str
scopes: list[str]
def get_pinned_session():
ctx = ssl.create_default_context()
# Enforce hostname verification and pin certificate fingerprint in production
return ctx
async def verify_token(token: str):
# Use a trusted, pinned session to fetch JWKS
async with httpx.AsyncClient(verify=ssl.create_default_context()) as client:
resp = await client.get(JWKS_URL, timeout=5.0)
resp.raise_for_status()
jwks = resp.json()
# Decode header to get kid
try:
unverified_header = jwt.get_unverified_header(token)
key = None
for k in jwks.get("keys", []):
if k["kid"] == unverified_header["kid"]:
key = jwt.algorithms.rsa_key_class(k)
break
if key is None:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Key not found")
# Strict validation: specify issuer and audience
payload = jwt.decode(
token,
key=key,
algorithms=["RS256"],
audience="myapi.example.com",
issuer="https://auth.example.com/",
options={"require": ["exp", "iss", "aud"]},
)
return TokenData(sub=payload["sub"], scopes=payload.get("scopes", []))
except JWTError as e:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=str(e))
@app.get("/secure")
async def read_secure(token: str = Depends(lambda req: req.headers.get("Authorization"))):
if token and token.startswith("Bearer "):
token = token[7:]
else:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
user = await verify_token(token)
return {"user": user.sub, "scopes": user.scopes}
2. Use HTTPS and enforce certificate pinning for outbound calls
When your FastAPI app makes outbound calls to validate tokens or to communicate with identity providers, enforce HTTPS with certificate pinning or strict hostname verification to reduce reliance on DNS resolution that could be poisoned.
import httpx
import ssl
# Enforce strict verification and optionally pin certificate fingerprints
ssl_context = ssl.create_default_context()
# Example: load a pinned certificate
# ssl_context.load_verify_locations(cafile="/path/to/pinned-ca.pem")
async def fetch_jwks():
async with httpx.AsyncClient(verify=ssl_context) as client:
r = await client.get("https://auth.example.com/.well-known/jwks.json", timeout=5.0)
r.raise_for_status()
return r.json()
3. Avoid dynamic hostnames for critical endpoints
Do not construct hostnames from user input or configuration that can be influenced at runtime. Use constants or configuration values that are validated and audited. If service discovery is required, perform discovery at startup and cache resolved endpoints with integrity checks.
# Good: static, validated endpoint
AUTH_ISSUER = "https://auth.example.com"
# Avoid: building URLs from untrusted input
# user_host = get_user_input() # dangerous
# url = f"https://{user_host}/.well-known/jwks.json"
These practices reduce the attack surface related to DNS cache poisoning and help ensure that JWT tokens are validated against trusted, integrity-protected endpoints. middleBrick’s scans, including the SSRF and LLM/AI Security checks, can help detect risky endpoint configurations and unsafe consumption patterns that may expose token handling flows.