Overview
Affected Versions
CVE-2021-32677: FastAPI < 0.65.2; CVE-2024-40627: OpaMiddleware < 2.0.1; CVE-2024-42816: fastapi-admin pro <= 0.1.4
Code Fix Example
# Vulnerable and fixed code snippets demonstrating the three CVEs side by side
# Note: This is a concise illustrative example; adapt to your app structure.
# Vulnerable pattern for CVE-2021-32677 (parsing JSON regardless of Content-Type)
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
app = FastAPI()
@app.post("/csfr-prone")
async def csfr_prone(request: Request):
data = await request.json() # vulnerability: parses JSON even if Content-Type isn't JSON
return {"received": data}
# Fixed pattern for CVE-2021-32677: guard JSON parsing on Content-Type
@app.post("/csfr-safe")
async def csfr_safe(request: Request):
ctype = request.headers.get("content-type", "")
if "application/json" not in ctype:
return JSONResponse({"detail": "Unsupported Content-Type"}, status_code=400)
data = await request.json()
return {"received": data}
# Vulnerable pattern for CVE-2024-40627: OPTIONS bypass in OpaMiddleware (illustrative)
from fastapi.responses import JSONResponse
@app.middleware("http")
async def opa_vuln(request: Request, call_next):
if request.method == "OPTIONS":
return JSONResponse({"detail": "OK"}, status_code=200) # no policy check
return await call_next(request)
# Fixed pattern for CVE-2024-40627: enforce auth/policy on OPTIONS
@app.middleware("http")
async def opa_fixed(request: Request, call_next):
if request.method == "OPTIONS" and not request.headers.get("Authorization"):
return JSONResponse({"detail": "Unauthorized"}, status_code=401)
return await call_next(request)
# Vulnerable pattern for CVE-2024-42816: unescaped user input rendered in HTML
from fastapi.responses import HTMLResponse
@app.get("/vuln-xss")
async def vuln_xss(request: Request):
name = request.query_params.get("name", "")
html = f"<div>Product: {name}</div>" # vulnerable: injects input directly
return HTMLResponse(content=html)
# Fixed pattern for CVE-2024-42816: escape/render via template (automatic escaping)
from fastapi.templating import Jinja2Templates
from fastapi import Depends
templates = Jinja2Templates(directory="templates")
@app.get("/fixed-xss")
async def fixed_xss(request: Request, _=Depends(lambda: None)):
name = request.query_params.get("name", "")
return templates.TemplateResponse("product.html", {"request": request, "name": name})
# Note: Ensure templates/product.html uses proper escaping (default in Jinja2).