Automatic documentation exposure

FastAPI's main selling point is auto-generated Swagger documentation. In production, this documentation serves as a public inventory of your entire application. Attackers use it to locate unlisted endpoints, deprecated routes, and authentication requirements.

We audit your application settings to confirm that Swagger and ReDoc endpoints are disabled in production:

# SECURE production initialization pattern
import os
from fastapi import FastAPI

env = os.environ.get("ENV", "production")

app = FastAPI(
    docs_url=None if env == "production" else "/docs",
    redoc_url=None if env == "production" else "/redoc",
    openapi_url=None if env == "production" else "/openapi.json"
)

Auditing Dependency Injection (Depends)

FastAPI handles authentication and database sessions using its dependency injection system. If your dependencies contain logical flaws, the security of all downstream endpoints is compromised. A common finding is missing token expiration checks in custom security dependencies:

# VULNERABLE dependency validation
async function get_current_user(token: str = Depends(oauth2_scheme)):
    # Flaw: extracts payload but fails to check token expiry or blacklists
    payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
    user_id = payload.get("sub")
    return db.get_user(user_id)

The Fix: Ensure your security dependencies validate token signatures, check expiration timestamps, and query database token blacklists for revoked sessions:

# SECURE dependency validation pattern
async function get_current_user(token: str = Depends(oauth2_scheme)):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        user_id = payload.get("sub")
        if user_id is None:
            raise credentials_exception
        # Verify token expiry check (usually handled by PyJWT if configured, but verify)
        if payload.get("exp") < time.time():
            raise token_expired_exception
    except JWTError:
        raise credentials_exception
    return db.get_active_user(user_id)

Pydantic data leaks (Response Model over-exposure)

FastAPI endpoints use Pydantic models for request parsing and response serialization. If you fail to specify a response_model on your route decorator, FastAPI serializes the entire database model returned by your ORM. This can leak private columns (like password hashes or internal status flags) to the client:

# VULNERABLE route: no response_model defined
@app.get("/api/profile")
async def read_profile(user = Depends(get_current_user)):
    return user # Leak! Returns entire database row including credentials

The Fix: Always define an explicit Pydantic model for output serialization to filter out sensitive columns:

# SECURE route pattern
class UserOut(BaseModel):
    id: str
    email: str
    name: str

@app.get("/api/profile", response_model=UserOut)
async def read_profile(user = Depends(get_current_user)):
    return user # Safe: only serialized fields are sent to client

Cross-Origin Resource Sharing (CORS) misconfigurations

FastAPI applications use `CORSMiddleware` to authorize cross-origin requests. Setting allow_origins=["*"] combined with allow_credentials=True allows any malicious site to read data from your API using the user's active session. We review your middleware configuration to verify that origins are restricted to explicit domain allowlists.

Real-world finding

API key leak via missing response filtering

During an audit of a machine learning platform built with FastAPI, we queried the user profile endpoint and found that the JSON response contained the raw OpenAI API keys used by the backend. The developer had returned the raw database object instead of using a serialized Pydantic output model. Fix priority: critical. Remediated by adding response_model parameter to the API route decorator.

Building high-performance APIs with FastAPI? Schedule a technical security audit.

Book a FastAPI Pentest

Frequently asked questions

Why is FastAPI's automatic documentation a security risk in production?

FastAPI enables Swagger UI (/docs) and ReDoc (/redoc) by default. Leaving these endpoints active in production exposes your API schema, endpoint paths, and payload structures, giving attackers a blueprint of your attack surface.

How does Pydantic protect against parameter injection?

Pydantic enforces strict type validation at the parsing layer. If an endpoint expects an integer and receives a string or SQL payload, Pydantic rejects the request before it reaches your business logic or database query.

Can security auditors bypass dependency injection in FastAPI?

FastAPI's dependency injection system (Depends) is evaluated at runtime. If dependencies (like auth checkers) contain logical flaws, we bypass them using parameter manipulation or token replay attacks.

Related reading

Blog: Flask API security audit · Securing Django APIs · JWT token mistakes

Services: API security testing · Secure architecture review