FastAPI
FastAPI sits between the two clusters either side of it. Django ships most protections on and the work is finding what got switched off. Express ships almost nothing and every control is something you add. FastAPI ships good defaults and almost no middleware — the settings it has are mostly right, and the things it does not have are simply absent.
So this cluster splits cleanly in two, and it is worth knowing which half a page is in before you read it.
Defaults that are already correct, where the page is a verify: debug is False,
response_model filtering works when declared, cookies are signed, and there is no
CORS policy at all until you add one.
Things that do not exist, where the page is a decision: no CSRF, no rate limiting, no request size cap, no host validation until you install it.
The mistake this cluster is built around is treating the second list as the first — concluding that because a framework does not ship a control, the control is not needed.
Check what you are actually running
Section titled “Check what you are actually running”python3 -c "import fastapi, starlette, pydantic; print(fastapi.__version__, starlette.__version__, pydantic.VERSION)"pip list 2>/dev/null | grep -iE '^(fastapi|starlette|uvicorn|pydantic|python-multipart) 'FastAPI 0.141.0 is current, on Starlette 1.3.1. Two version boundaries are
worth knowing about. FastAPI requires Python 3.10+ from recent releases, so an
environment on 3.9 will silently resolve to a much older FastAPI — pip install fastapi succeeding is not evidence you got a current one, and the version command
above is the check. And Pydantic 2 changed validation error shapes from v1, which
matters for
validation error disclosure.
One pair to grep for first
Section titled “One pair to grep for first”Before anything else on this page:
grep -rn -A6 'CORSMiddleware' app/ | grep -E 'allow_origins|allow_credentials'allow_origins=["*"] together with allow_credentials=True does not send a wildcard.
Starlette reflects the caller’s origin instead, which means any website can make
authenticated requests with your users’ cookies and read the responses. It is the
cluster’s threat page, and it is first because the
dangerous configuration is one people arrive at by fixing a browser error rather than by
choosing it.
One setting underneath three controls
Section titled “One setting underneath three controls”request.client.host is either the peer address or a value the caller wrote in a
header, and uvicorn’s --forwarded-allow-ips decides which. Rate limiting, IP
allow-lists and every audit log inherit that decision, and both of the obvious values
are wrong in a container: the 127.0.0.1 default ignores your proxy, and the '*'
that fixes it trusts everyone.
Read proxy headers before the pages that depend on it.
The checklist
Section titled “The checklist”Severity is the exposure a control closes, not how often it gets recommended. Two
entries are rated low and say so on the page — including validation error disclosure,
where the response mostly goes back to the client that sent it, and the real risk is
what your logs then keep.
What this cluster does not cover
Section titled “What this cluster does not cover”Python itself, the ASGI server’s TLS, the process manager, and the proxy in front of you. That is the server’s half rather than the application’s — see application vs server for the dividing line, and for the three controls that genuinely span both.