Http Request Smuggling in Fastapi with Mongodb
Http Request Smuggling in Fastapi with Mongodb — how this specific combination creates or exposes the vulnerability
HTTP request smuggling arises when an API server and an intermediary (such as a load balancer or reverse proxy) disagree on how to parse and forward a request. In a Fastapi application backed by Mongodb, the risk is not in the database driver itself, but in how Fastapi handles request streams and how you compose responses that may be reused across pipeline stages.
Fastapi relies on Starlette for HTTP handling and uses body parsing, file uploads, and streaming endpoints. If you accept and forward raw request data (for example, passing a raw body to an internal service or including user-controlled values in X-Original-URL or X-Forwarded-For headers), and you also use Mongodb operations that depend on unchecked request-derived parameters, the combination can expose smuggling surfaces.
Consider an endpoint that accepts a raw body and stores metadata in Mongodb based on headers controlled by the client:
from fastapi import FastAPI, Request, Header, HTTPException
from pymongo import MongoClient
app = FastAPI()
client = MongoClient("mongodb://localhost:27017")
db = client["auditdb"]
@app.post("/log")
async def log_request(raw: bytes = Request().body(), x_trace_id: str = Header(None)):
# Example of storing request metadata derived from headers/body
db["requests"].insert_one({
"trace_id": x_trace_id,
"body_size": len(raw),
"origin": "untrusted"
})
return {"stored": True}
If this endpoint is placed behind a reverse proxy that uses header-based routing or retries, and the application reuses the same path for internal routing (for instance via X-Original-URL or a custom header used to select a Mongodb collection name), an attacker can craft a request that is interpreted differently by the proxy and by Fastapi. A typical smuggler might send:
POST /log HTTP/1.1
Content-Length: 5
X-Target-Collection: users
hello
Where Fastapi reads Content-Length as 5 and forwards or logs based on X-Target-Collection, while a proxy might route based on a different header interpretation. If the header is later used to pick a Mongodb collection without validation, you risk unintended data access or injection across trust boundaries.
In practice, the smuggling risk with Fastapi + Mongodb is elevated when you:
- Accept raw request bodies and forward them internally without strict length and framing validation.
- Derive Mongodb collection names, database names, or query filters from headers or path segments controlled by the client.
- Use ambiguous header handling (e.g., case-insensitive duplicates) where Fastapi and the proxy disagree on which value to use.
These patterns do not exploit a bug in Fastapi or Mongodb drivers, but they expose an application-level mismatch in request interpretation that can lead to data leakage or unauthorized operations across internal services.
Mongodb-Specific Remediation in Fastapi — concrete code fixes
Remediation focuses on strict input validation, avoiding header or path values that directly control database routing, and ensuring consistent request parsing between Fastapi and any intermediaries.
1. Validate and sanitize any value used to select a Mongodb collection or database. Do not trust headers or query parameters for collection names. Use an allowlist:
from fastapi import FastAPI, Header, HTTPException
from pymongo import MongoClient
ALLOWED_COLLECTIONS = {"users", "orders", "events"}
client = MongoClient("mongodb://localhost:27017")
def get_collection(name: str):
if name not in ALLOWED_COLLECTIONS:
raise HTTPException(status_code=400, detail="Invalid collection")
return client[name]
@app.post("/store")
async def store(collection: str, data: dict):
coll = get_collection(collection)
result = coll.insert_one(data)
return {"id": str(result.inserted_id)}
2. Use explicit body models and avoid raw bytes forwarding. Let Pydantic parse known structures and do not forward raw bodies to internal endpoints or use them to derive database operations:
from fastapi import FastAPI
from pydantic import BaseModel
from pymongo import MongoClient
app = FastAPI()
client = MongoClient("mongodb://localhost:27017")
db = client["appdb"]
class LogEntry(BaseModel):
message: str
level: str
@app.post("/entries")
async def create_entry(entry: LogEntry):
result = db["entries"].insert_one(entry.dict())
return {"id": str(result.inserted_id)}
3. Normalize and canonicalize headers before using them in database logic. If you must use headers, ensure case-insensitive duplicates are handled consistently and values are constrained:
from fastapi import Request
def safe_header(req: Request, name: str, allowed_values):
values = req.headers.get_all(name)
if not values:
return None
chosen = values[-1] # or first, depending on policy
if chosen not in allowed_values:
return None
return chosen
4. Configure proxy headers carefully and disable header forwarding when unnecessary. In your proxy (e.g., Nginx, Traefik), avoid passing client-controlled X-Forwarded-* headers to Fastapi unless you explicitly validate and sanitize them. Fastapi’s trusted_hosts and proxy_headers settings should align with your infrastructure’s routing rules.
5. Apply principle of least privilege to Mongodb connections. Use distinct database users with minimal permissions for each Fastapi route or service account, so even if smuggling leads to unintended queries, the blast radius is limited.
These steps reduce the likelihood of request interpretation mismatches and ensure that Mongodb interactions are driven by validated data rather than mutable headers or unchecked request metadata.