Man In The Middle in Fastapi with Api Keys
Man In The Middle in Fastapi with Api Keys — how this specific combination creates or exposes the vulnerability
A Man In The Middle (MitM) scenario in FastAPI with API keys occurs when an attacker can intercept or alter traffic between the client and the API endpoint. If API keys are transmitted in cleartext over insecure channels, an on-path attacker can observe and reuse them. Even when API keys are passed in headers, failing to enforce HTTPS or using HTTP redirects can expose keys to interception, enabling the attacker to impersonate legitimate clients and call protected endpoints.
Consider a FastAPI service that uses a simple X-API-Key header for authentication without requiring TLS. An attacker operating on the same network can capture requests, extract the key, and replay authorized requests. Additionally, if the API accepts HTTP and redirects to HTTPS improperly, an attacker can perform a downgrade or strip the secure protocol, placing themselves between client and server. This exposes not only the API key but also any data the endpoint processes, which can be leveraged for further attacks such as data exfiltration or privilege abuse.
In some cases, API keys are embedded in client-side code or stored insecurely, making them susceptible to extraction via compromised systems or misconfigured storage. When combined with insufficient transport security, such practices widen the attack surface for MitM. Because middleBrick scans the unauthenticated attack surface, it will flag endpoints that accept API keys without enforcing encryption and robust transport checks, highlighting the risk of interception and unauthorized access.
Api Keys-Specific Remediation in Fastapi — concrete code fixes
To mitigate MitM risks with API keys in FastAPI, enforce HTTPS for all routes and validate the presence and correctness of API keys on each request. Below are concrete, working examples that demonstrate secure handling of API keys in FastAPI.
from fastapi import FastAPI, Depends, HTTPException, Header
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
import os
app = FastAPI()
# Require HTTPS in production by enforcing it via middleware or deployment.
# Example: Reject requests that do not use HTTPS when configured for strict transport.
STRICT_HTTPS = os.getenv("STRICT_HTTPS", "true").lower() == "true"
if STRICT_HTTPS:
@app.middleware("http")
async def enforce_https(request, call_next):
if request.url.scheme != "https":
# Return an error instead of redirecting to avoid potential downgrade risks
from starlette.responses import JSONResponse
return JSONResponse({"detail": "HTTPS required"}, status_code=403)
def get_api_key(credentials: HTTPAuthorizationCredentials = Header(None)):
# Expects: Authorization: ApiKey
expected_prefix = "ApiKey "
if not credentials or not credentials.credentials.startswith(expected_prefix):
raise HTTPException(status_code=401, detail="Invalid authentication scheme")
key = credentials.credentials[len(expected_prefix):]
if not key:
raise HTTPException(status_code=401, detail="API key missing")
# Validate against a secure store; this example uses environment variable
valid_key = os.getenv("API_KEY")
if valid_key is None or key != valid_key:
raise HTTPException(status_code=403, detail="Forbidden")
return key
@app.get("/secure-data")
def read_secure_data(api_key: str = Depends(get_api_key)):
# Key is verified; proceed with business logic
return {"message": "Access granted", "key_present": bool(api_key)}
# Alternative: using HTTPAuthorization with scheme "ApiKey"
def get_api_key_bearer(credentials: HTTPAuthorizationCredentials = Header(None)):
# Accepts Authorization: ApiKey or Authorization: Bearer
if not credentials:
raise HTTPException(status_code=401, detail="Authorization header required")
scheme, _, token = credentials.credentials.partition(" ")
if scheme not in ("ApiKey", "Bearer") or not token:
raise HTTPException(status_code=401, detail="Invalid authorization header format")
valid_key = os.getenv("API_KEY")
if token != valid_key:
raise HTTPException(status_code=403, detail="Forbidden")
return token
@app.get("/items")
def list_items(key: str = Depends(get_api_key_bearer)):
return {"items": ["item-a", "item-b"]}
# Client example:
# curl -H "Authorization: ApiKey YOUR_SECURE_KEY" https://your-api.example.com/secure-data
# Ensure DNS and certificates are correctly configured to prevent SSL stripping.
These examples emphasize transport integrity by requiring HTTPS and validating API keys on each request. They avoid insecure practices such as logging keys, embedding them in URLs, or accepting them without a strict prefix. Deploy the service behind a termination point that enforces TLS and consider additional network-level protections to reduce the risk of MitM.