Chapter 31 · Security Best Practices
Subchapter 31.9
references/python-fastapi-web-server-security.mdMarkdown44 KBView on GitHub
Also bundled
OpenAIThis document is designed as a security spec that supports:
It is intentionally written as a set of normative requirements (“MUST/SHOULD/MAY”) plus audit rules (what bad patterns look like, how to detect them, and how to fix/mitigate them).
FastAPI is commonly deployed with an ASGI server (e.g., Uvicorn) and is built on Starlette + Pydantic, so this spec covers those layers where they affect security. (PyPI (opens in a new tab))
MUST NOT request, output, log, or commit secrets (API keys, passwords, private keys, session cookies, signing keys, database URLs with credentials).
MUST NOT “fix” security by disabling protections (e.g., weakening auth, making CORS permissive, skipping signature checks, disabling validation, turning off TLS verification, adding allow_origins=["*"] with credentials).
MUST provide evidence-based findings during audits: cite file paths, code snippets, and configuration values that justify the claim.
MUST treat uncertainty honestly: if a protection might exist in infrastructure (reverse proxy, WAF, CDN, service mesh), report it as “not visible in app code; verify at runtime/config”.
MUST treat browser controls correctly:
When asked to write new FastAPI code or modify existing code:
While working anywhere in a FastAPI repo (even if the user did not ask for a security scan):
When the user asks to “scan”, “audit”, or “hunt for vulns”:
Recommended audit order:
Examples include:
Host, Origin, X-Forwarded-*)A request is state-changing if it can create/update/delete data, change auth/session state, trigger side effects (purchase, email send, webhook send), or initiate privileged actions.
For each issue found, output:
This is the smallest “production baseline” that prevents common FastAPI/ASGI misconfigurations.
Baseline goals:
Each rule contains: required practice, insecure patterns, detection hints, and remediation.
Severity: High (if production)
Required:
Insecure patterns:
uvicorn ... --reload (or equivalent “reload=True” configs) in production entrypoints.--reload in production.Detection hints:
--reload, reload=True, watchfiles, fastapi dev, “development” run scripts.Fix:
Note:
Severity: Critical
Required:
Insecure patterns:
app = FastAPI(debug=True) (or Starlette debug=True), or equivalent environment toggles enabling debug in production. (PyPI (opens in a new tab))Detection hints:
debug=True, DEBUG = True, environment flags mapped to debug.Fix:
Severity: Medium (can be High in sensitive/internal apps)
Required:
/docs, /redoc, and /openapi.json in production for public-facing services unless there is an explicit business need.Insecure patterns:
/docs and /openapi.json for internal/admin APIs.Detection hints:
FastAPI(docs_url=..., redoc_url=..., openapi_url=...) or defaults.Fix:
docs_url=None, redoc_url=None, openapi_url=None) or restrict access at the edge.Severity: High
Required:
APIRouter for authenticated endpoints). (FastAPI (opens in a new tab))Insecure patterns:
Detection hints:
Depends(...)/Security(...).if user is None: raise ... inside handlers (instead of dependencies).Fix:
Depends()/Security(). (FastAPI (opens in a new tab))Severity: High
Required:
Authorization: Bearer <token> header for token auth, not query parameters. (FastAPI (opens in a new tab))Insecure patterns:
?token=..., ?api_key=..., ?auth=... used for primary auth.Detection hints:
token, api_key, key, secret, password.Fix:
Severity: Critical
Required:
Insecure patterns:
Detection hints:
password= persisted fields, and look for hashlib.md5/sha1/sha256 usage on passwords.Fix:
Severity: High
Required:
exp; typically also iss/aud if multi-service or multi-tenant).Insecure patterns:
jwt.decode(..., options={"verify_signature": False}) or equivalent.alg=none / algorithm confusion.Detection hints:
jwt.decode, python-jose, PyJWT, verify_signature.Fix:
Severity: High
Required:
Insecure patterns:
GET /users/{id} returns user record without verifying caller can access that id.Detection hints:
Fix:
Severity: High (only if TLS is enabled)
Required (production, HTTPS):
Secure in production environment when TLS is configured. When running in a local dev environment over HTTP, do not set Secure property on cookies. You should do this conditionally based on if the app is running in production mode. You should also include a property like SESSION_COOKIE_SECURE which can be used to disable Secure cookies when testing over HTTP.SameSite=Lax (or Strict if UX allows); if you require cross-site cookies, document the CSRF implications and add compensating controls. (OWASP Cheat Sheet Series (opens in a new tab))SessionMiddleware, MUST set https_only=True in production and choose an appropriate same_site. (PyPI (opens in a new tab))Insecure patterns:
SameSite=None cookies used for authenticated state-changing endpoints without CSRF protections.Detection hints:
SessionMiddleware( and inspect parameters like https_only, same_site.set_cookie( usage and cookie flags.Fix:
Severity: High
Required:
Insecure patterns:
Detection hints:
request.session[...] = or session[...] =-equivalent patterns; identify what is stored.SessionMiddleware or other cookie session mechanisms.Fix:
Severity: High
Note: This only applies if using cookie based auth. If the application uses header or token based auth such as Authorization header, then CSRF is not an issue.
Required:
Authorization header), CSRF is usually not applicable. (FastAPI (opens in a new tab))Insecure patterns:
Detection hints:
Fix:
Severity: Medium (especially for APIs that write to DB)
Required:
dict/Any.Insecure patterns:
payload = await request.json() followed by Model(**payload) or direct DB writes with payload (no allowlist).Detection hints:
await request.json(), request.body(), dict-typed bodies, Any-typed bodies.db.update(**payload) or Model(**payload) with unfiltered input.Fix:
Severity: Medium
Required:
Insecure patterns:
password_hash, is_admin, etc).Detection hints:
return user where user is an ORM instance.response_model omissions on endpoints that return sensitive resources.Fix:
Severity: High (if the service serves HTML)
Required:
Insecure patterns:
Detection hints:
Fix:
Note:
Severity: Critical
Required:
MUST NOT render templates that contain user-controlled template syntax.
MUST treat “template-from-string” rendering as dangerous if influenced by untrusted input.
If untrusted templates are absolutely required (rare, high-risk):
Insecure patterns:
Detection hints:
Environment.from_string, Template(...), or similar.Fix:
Severity: Medium
Required (typical API/web app):
SHOULD set:
X-Content-Type-Options: nosniffX-Frame-Options and/or CSP frame-ancestors) if HTML is servedReferrer-Policy and Permissions-Policy as appropriateNOTE:
Insecure patterns:
Detection hints:
Fix:
Severity: Medium (High if misconfigured with credentials)
Required:
If CORS is not needed, MUST keep it disabled.
If CORS is needed:
Insecure patterns:
allow_origins=["*"] together with allow_credentials=True.Origin without validation.allow_origin_regex=".*" used broadly.Detection hints:
CORSMiddleware configuration.allow_origins=["*"], allow_credentials=True, allow_origin_regex.Fix:
Severity: Low
Required:
TrustedHostMiddleware (or equivalent at edge) to restrict accepted Host values. (PyPI (opens in a new tab))Host header for security-sensitive decisions without validation.Insecure patterns:
Detection hints:
TrustedHostMiddleware usage.request.url, request.base_url, or host-derived values to build external URLs.Fix:
Severity: High (when behind a proxy)
Required:
X-Forwarded-* headers from the open internet.Insecure patterns:
Detection hints:
--proxy-headers, --forwarded-allow-ips, or equivalent config.request.client.host, request.url.scheme, request.headers["x-forwarded-for"].Fix:
forwarded_allow_ips to that proxy. (PyPI (opens in a new tab))Severity: Low
Required:
Insecure patterns:
Detection hints:
multipart/form-data usage.client_max_body_size, ALB limits, etc.) and missing app-level checks.Fix:
Severity: High
Required:
FileResponse/filesystem calls without strict validation and safe base directories.StaticFiles, MUST keep Starlette updated and understand the security history (path traversal advisory exists for older versions). (advisories.gitlab.com (opens in a new tab))Insecure patterns:
FileResponse(request.query_params["path"])StaticFiles(directory="uploads") where uploads include HTML/JS/SVG and are served inline.Detection hints:
FileResponse(, StaticFiles(, open( in routes.Fix:
Severity: Low (if affected versions and file serving is enabled)
Required:
FileResponse/StaticFiles.Range header handling and file serving as a DoS surface. (advisories.gitlab.com (opens in a new tab))Insecure patterns:
Detection hints:
FileResponse and StaticFiles.Fix:
Severity: Medium
Required:
Insecure patterns:
Detection hints:
Fix:
Severity: High
Required:
Insecure patterns:
f"SELECT ... WHERE id={user_id}""... WHERE name = '%s'" % user_inputDetection hints:
.execute(...).Fix:
Severity: Critical to High (depends on exposure)
Required:
MUST avoid executing shell commands with untrusted input.
If subprocess is necessary:
shell=True with attacker-influenced stringsInsecure patterns:
os.system(user_input)subprocess.run(f"cmd {user}", shell=True)bash -c, sh -c, PowerShell, etc.Detection hints:
os.system, subprocess, Popen, shell=True.Fix:
-- separator where supported. (OWASP Cheat Sheet Series (opens in a new tab))Severity: Medium (can be High in cloud/VPC environments)
Required:
Insecure patterns:
httpx.get(request.query_params["url"])Detection hints:
requests, httpx, urllib, aiohttp calls with URLs derived from requests/DB.fetch, preview, proxy, webhook, import.Fix:
Severity: Low
Required:
next, redirect, return_to).Insecure patterns:
RedirectResponse(next) where next is user-controlled with no validation.Detection hints:
RedirectResponse( or redirect logic and examine the source of the target.Fix:
Severity: Medium to High (depends on data/privilege)
Required:
Insecure patterns:
@app.websocket(...) accepts and trusts the connection with no auth check.Detection hints:
@app.websocket / websocket_endpoint and inspect whether auth is performed before accepting sensitive operations.Fix:
Severity: Low
Required:
Audit focus examples (historical):
Detection hints:
requirements.txt, lockfiles, container images, and runtime environments for actual installed versions.Fix:
When actively scanning, use these high-signal patterns:
Dev server / debug:
--reload, reload=True, debug=True, FastAPI(debug=True) (PyPI (opens in a new tab))OpenAPI/docs exposure:
/docs, /redoc, /openapi.json, docs_url=, openapi_url=Auth enforcement gaps:
Depends()/Security() where expected; routers without a consistent dependency boundary (FastAPI (opens in a new tab))token=, api_key=, key=) (FastAPI (opens in a new tab))Session/cookies + CSRF:
SessionMiddleware( and cookie flags (https_only, same_site) (PyPI (opens in a new tab))Input validation & mass assignment:
await request.json() and direct DB writes from dicts; models accepting extra fields (OWASP Cheat Sheet Series (opens in a new tab))Excessive data exposure:
response_model; responses containing password/role/internal fields (FastAPI (opens in a new tab))CORS:
CORSMiddleware with allow_origins=["*"], allow_origin_regex=".*", allow_credentials=True (OWASP Cheat Sheet Series (opens in a new tab))Files:
FileResponse( with user-controlled paths; StaticFiles( exposing uploads (advisories.gitlab.com (opens in a new tab))Uploads / multipart:
multipart/form-data endpoints with no size/field constraints; outdated Starlette/python-multipart (advisories.gitlab.com (opens in a new tab))Injection:
.execute(...) (OWASP Cheat Sheet Series (opens in a new tab))subprocess.*, shell=True, os.system (OWASP Cheat Sheet Series (opens in a new tab))SSRF:
httpx.get/post or requests.* with URL from request/DB, no allowlist/timeouts (OWASP Cheat Sheet Series (opens in a new tab))Redirect:
RedirectResponse(next) with no validation (OWASP Cheat Sheet Series (opens in a new tab))WebSockets:
@app.websocket handlers without auth/origin checks; use of ws:// in prod configs (FastAPI (opens in a new tab))Always try to confirm:
Primary framework documentation:
https://pypi.org/project/fastapi/ (PyPI (opens in a new tab))https://fastapi.tiangolo.com/tutorial/security/first-steps/ (FastAPI (opens in a new tab))Depends, Security) — https://fastapi.tiangolo.com/reference/dependencies/ (FastAPI (opens in a new tab))https://fastapi.tiangolo.com/reference/apirouter/ (FastAPI (opens in a new tab))https://fastapi.tiangolo.com/advanced/websockets/ (FastAPI (opens in a new tab))ASGI/server stack documentation:
https://pypi.org/project/starlette/ (PyPI (opens in a new tab))https://starlette.dev/websockets/ (Starlette (opens in a new tab))https://pypi.org/project/uvicorn/ (PyPI (opens in a new tab))https://docs.pydantic.dev/latest/ (Pydantic (opens in a new tab))Security standards and cheat sheets:
https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html (OWASP Cheat Sheet Series (opens in a new tab))https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html (OWASP Cheat Sheet Series (opens in a new tab))https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html (OWASP Cheat Sheet Series (opens in a new tab))https://cheatsheetseries.owasp.org/cheatsheets/Mass_Assignment_Cheat_Sheet.html (OWASP Cheat Sheet Series (opens in a new tab))https://owasp.org/API-Security/editions/2023/en/0x11-t10/ (OWASP Foundation (opens in a new tab))https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html (OWASP Cheat Sheet Series (opens in a new tab))https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html (OWASP Cheat Sheet Series (opens in a new tab))https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html (OWASP Cheat Sheet Series (opens in a new tab))https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html (OWASP Cheat Sheet Series (opens in a new tab))https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html (OWASP Cheat Sheet Series (opens in a new tab))https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html (OWASP Cheat Sheet Series (opens in a new tab))https://cheatsheetseries.owasp.org/cheatsheets/WebSocket_Security_Cheat_Sheet.html (OWASP Cheat Sheet Series (opens in a new tab))https://owasp.org/www-project-web-security-testing-guide/v41/4-Web_Application_Security_Testing/07-Input_Validation_Testing/18-Testing_for_Server_Side_Template_Injection (OWASP Foundation (opens in a new tab))https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/11-Client-side_Testing/10-Testing_WebSockets (OWASP Foundation (opens in a new tab))Template safety references:
https://jinja.palletsprojects.com/en/stable/sandbox/ (jinja.palletsprojects.com (opens in a new tab))Selected supply-chain/advisory references (Starlette examples):
https://advisories.gitlab.com/pkg/pypi/starlette/CVE-2023-29159/ (advisories.gitlab.com (opens in a new tab))https://advisories.gitlab.com/pkg/pypi/starlette/CVE-2024-47874/ (advisories.gitlab.com (opens in a new tab))https://advisories.gitlab.com/pkg/pypi/starlette/CVE-2025-62727/ (advisories.gitlab.com (opens in a new tab))This file