PyFu

Exposed FastAPI Documentation Page

Python-based Web Application Attacks

FastAPI automatically generates interactive API documentation using OpenAPI (Swagger UI) and ReDoc. By default, these documentation pages are exposed at /docs and /redoc endpoints without any authentication.

While this feature is extremely useful during development, leaving it enabled in production environments can lead to full API documentation leakage, exposing sensitive information about the application’s internal structure, endpoints, parameters, and data models.

This information disclosure significantly reduces the reconnaissance effort required for an attacker and provides a detailed roadmap for further exploitation.

Application Example

The following FastAPI application demonstrates a common scenario where sensitive administrative endpoints are exposed through the default documentation:

from fastapi import FastAPI, HTTPException, Header
from pydantic import BaseModel
from typing import Optional

app = FastAPI(
    title="Internal User Management API",
    description="API for managing user accounts and administrative operations",
    version="1.0.0"
)

# Simulated user database
users_db = {
    "admin": {"username": "admin", "role": "administrator", "api_key": "sk-admin-secret-key-12345"},
    "user1": {"username": "user1", "role": "user", "api_key": "sk-user-key-67890"}
}

class UserResponse(BaseModel):
    username: str
    role: str

class AdminConfig(BaseModel):
    debug_mode: bool
    database_url: str
    secret_key: str

@app.get("/api/v1/users/{username}", response_model=UserResponse)
def get_user(username: str, x_api_key: Optional[str] = Header(None)):
    """
    Retrieve user information by username.
    Requires valid API key in X-API-Key header.
    """
    if username not in users_db:
        raise HTTPException(status_code=404, detail="User not found")
    user = users_db[username]
    return UserResponse(username=user["username"], role=user["role"])

@app.get("/api/v1/admin/config", response_model=AdminConfig)
def get_admin_config(x_admin_token: str = Header(...)):
    """
    Retrieve sensitive application configuration.
    Restricted to administrators with valid admin token.
    Internal endpoint - do not expose publicly.
    """
    return AdminConfig(
        debug_mode=True,
        database_url="postgresql://admin:P@ssw0rd123@internal-db.local:5432/production",
        secret_key="jwt-signing-key-super-secret-value"
    )

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

Exploitation

This issue comes from how FastAPI initializes its documentation endpoints by default. When you create a FastAPI application:

app = FastAPI()

FastAPI automatically registers three documentation-related routes:

  • /docs - Swagger UI interactive documentation
  • /redoc - ReDoc alternative documentation interface
  • /openapi.json - Raw OpenAPI specification in JSON format

These endpoints are enabled without any authentication and are immediately accessible to anyone who can reach the application.

Looking at our vulnerable code, the initialization includes metadata that further enriches the exposed documentation:

app = FastAPI(
    title="Internal User Management API",
    description="API for managing user accounts and administrative operations",
    version="1.0.0"
)

This metadata appears directly in the /docs page, revealing the application’s purpose and internal naming conventions to any visitor.

When an attacker discovers the application running on port 8000, accessing the default documentation path:

http://target:8000/docs

The Swagger UI interface renders a complete interactive view of the API, exposing:

  1. The /api/v1/users/{username} endpoint with its authentication header requirement
  2. The /api/v1/admin/config endpoint that returns sensitive configuration data
  3. The exact header names required for authentication (X-API-Key, X-Admin-Token)
  4. Response schemas showing what data fields are returned

The /openapi.json endpoint provides the same information in a machine-readable format:

curl http://target:8000/openapi.json | jq

This JSON output can be parsed programmatically for automated reconnaissance and attack scripting.

Why exposed FastAPI docs matter from an offensive security perspective

Exposed docs are the first thing I look for when I find a FastAPI target, because they collapse the reconnaissance phase entirely. Instead of fuzzing routes and guessing parameter names, I pull /openapi.json and get the complete, machine-readable map of every endpoint, every required header, every request body schema, and every response field. That turns blind black-box testing into a precise, scripted attack plan. The internal endpoints that developers assume are obscure are the ones the schema advertises most clearly, like the /api/v1/admin/config route here that the comments explicitly mark as internal.

The schema does more than list paths. It tells me where the soft spots are: which routes take a header but never validate it, which ones return secrets, which version of the API I am hitting, and what the data models look like before I send a single malicious request. I look for these tells:

  • /docs, /redoc, or /openapi.json returning 200 on a production host or behind a production hostname.
  • Endpoint titles and descriptions that name internal systems, like “Internal User Management API”, which confirm I am on something that was never meant to be public.
  • Auth declared as a bare Header(...) with no dependency that checks it, which signals a header-present-but-unvalidated control I can satisfy with any value.
  • Admin, debug, config, or internal paths in the paths list that the schema hands me for free.

The defender takeaway: treat the OpenAPI schema as a published attack plan, and disable or authenticate docs_url, redoc_url, and openapi_url in production.

Proof of exploitation

Run the lab app (PyFuLabs/fastapi-fu/fastapi-docs-exposure). The interactive docs are live and the OpenAPI schema enumerates an endpoint the comments call internal:

curl -s -o /dev/null -w "/docs -> HTTP %{http_code}\n" "http://pyfu.local/fastapi-fu/fastapi-docs-exposure/docs"
curl -s "http://pyfu.local/fastapi-fu/fastapi-docs-exposure/openapi.json" | jq -r '.paths | keys[]'
/docs -> HTTP 200
/api/v1/users/{username}
/api/v1/admin/config

That admin endpoint’s only control is requiring a header, which is never validated, so any value leaks the secrets:

curl -s "http://pyfu.local/fastapi-fu/fastapi-docs-exposure/api/v1/admin/config" -H "x-admin-token: anything"
{"debug_mode":true,"database_url":"postgresql://admin:P@ssw0rd123@internal-db.local:5432/production","secret_key":"jwt-signing-key-super-secret-value"}

Mitigation

To prevent documentation exposure in production, disable the automatic docs generation:

app = FastAPI(docs_url=None, redoc_url=None, openapi_url=None)

Alternatively, protect the documentation endpoints with authentication or restrict access to internal networks only.