HIGH insecure designfastapi

Insecure Design in Fastapi

How Insecure Design Manifests in Fastapi

Insecure design in Fastapi applications often emerges from architectural decisions that prioritize functionality over security. Fastapi's async-first design and automatic OpenAPI generation can create hidden attack surfaces when developers don't consider security implications during the design phase.

One common manifestation is improper dependency injection. Fastapi's dependency injection system is powerful but can be misused. Consider this vulnerable pattern:

from fastapi import FastAPI, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from database import get_async_db

app = FastAPI()

@app.get('/users/{user_id}')
async def get_user(user_id: int, db: AsyncSession = Depends(get_async_db)):
    # No authorization check - any authenticated user can access any user data
    user = await db.get(User, user_id)
    return user

This design assumes all authenticated users should have access to all user data, violating the principle of least privilege. Fastapi's dependency injection makes it easy to forget authorization checks when focusing on building the data access layer.

Another Fastapi-specific design flaw involves Pydantic model exposure. Fastapi automatically generates OpenAPI schemas from Pydantic models, but developers often expose internal models without considering what data they reveal:

from pydantic import BaseModel
from fastapi import FastAPI

class User(BaseModel):
    id: int
    email: str
    password_hash: str  # Exposed in API schema!
    is_admin: bool

app = FastAPI()

@app.get('/users/me', response_model=User)
async def get_current_user(current_user: User = Depends(get_current_user)):
    return current_user

The password_hash and is_admin fields are exposed in the OpenAPI documentation, revealing internal implementation details. Fastapi's automatic schema generation means sensitive fields can be inadvertently documented.

Fastapi's background tasks feature can also introduce insecure design patterns. Background tasks run outside the request context, making it easy to create race conditions or bypass security checks:

from fastapi import BackgroundTasks

@app.post('/process-data')
async def process_data(data: dict, background_tasks: BackgroundTasks):
    # Background task has no context about the original request
    background_tasks.add_task(process_in_background, data)
    return {'status': 'processing'}

async def process_in_background(data: dict):
    # No authentication/authorization context available
    await expensive_operation(data)

The background task cannot verify the original request's authentication context, creating a design gap where security boundaries are crossed.

Fastapi-Specific Detection

Detecting insecure design in Fastapi applications requires examining both the code structure and the generated OpenAPI specifications. middleBrick's Fastapi-specific scanning identifies these patterns by analyzing the runtime behavior and comparing it against the OpenAPI schema.

For dependency injection vulnerabilities, middleBrick traces the dependency graph to identify endpoints that lack proper authorization checks. It analyzes the dependency injection patterns to find where database sessions or other sensitive resources are injected without proper access controls:

# middleBrick detection pattern
# Looks for endpoints with database dependencies but no authorization
@app.get('/vulnerable/{id}')
async def vulnerable_endpoint(id: int, db: AsyncSession = Depends(get_db)):
    # Missing authorization check - detected as insecure design
    return await db.get(SomeModel, id)

The scanner identifies that the endpoint accepts a database dependency but doesn't verify whether the requester has permission to access the specific resource.

For Pydantic model exposure, middleBrick analyzes the OpenAPI schema generation to identify sensitive fields that are unnecessarily exposed. It cross-references the schema with common sensitive field patterns:

# middleBrick analysis
# Detects exposed sensitive fields in response models
class ExposedModel(BaseModel):
    id: int
    password_hash: str  # Detected as sensitive exposure
    ssn: str           # Detected as sensitive exposure
    internal_notes: str # Detected as sensitive exposure

The scanner flags models containing fields like password_hash, ssn, internal_notes, or other PII that shouldn't be exposed through the API.

middleBrick also tests for background task security gaps by analyzing how background tasks are implemented and whether they maintain proper security context. It looks for patterns where background tasks might execute operations without proper authorization:

# middleBrick detection
# Identifies background tasks without security context
@app.post('/process')
async def process(data: dict, background_tasks: BackgroundTasks):
    background_tasks.add_task(risky_operation, data)
    return {'status': 'queued'}

async def risky_operation(data: dict):
    # No auth context - potential security gap
    await process_sensitive_data(data)

The scanner flags background tasks that could potentially execute operations without the original request's security context.

Fastapi-Specific Remediation

Fastapi provides several native mechanisms to address insecure design patterns. For dependency injection authorization, Fastapi's dependency system can be enhanced with proper access control:

from fastapi import Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from database import get_async_db
from models import User, get_user_by_id
from auth import get_current_active_user

async def get_resource_with_auth(
    resource_id: int,
    current_user: User = Depends(get_current_active_user),
    db: AsyncSession = Depends(get_async_db)
):
    """Dependency that ensures user has access to resource"""
    resource = await db.get(Resource, resource_id)
    if not resource:
        raise HTTPException(status_code=404, detail="Resource not found")
    
    if not await check_user_access(current_user, resource):
        raise HTTPException(status_code=403, detail="Access denied")
    
    return resource

@app.get('/resources/{resource_id}')
async def get_resource(resource: Resource = Depends(get_resource_with_auth)):
    return resource

This pattern ensures authorization is enforced at the dependency level, making it impossible to access resources without proper permissions.

For Pydantic model exposure, Fastapi's response_model_exclude and response_model_include parameters provide fine-grained control over what data is exposed:

from pydantic import BaseModel
from fastapi import FastAPI

class UserInternal(BaseModel):
    id: int
    email: str
    password_hash: str
    is_admin: bool
    ssn: str
    internal_notes: str

class UserPublic(BaseModel):
    id: int
    email: str

app = FastAPI()

@app.get('/users/me', response_model=UserPublic)
async def get_current_user(current_user: UserInternal = Depends(get_current_user)):
    return current_user

@app.get('/users/{user_id}', response_model=UserInternal, 
         response_model_exclude={'password_hash', 'ssn', 'internal_notes'})
async def get_user(user_id: int, db: AsyncSession = Depends(get_async_db)):
    return await db.get(User, user_id)

This approach separates internal data models from public API contracts, ensuring sensitive fields are never exposed.

For background task security, Fastapi's BackgroundTasks can be used with proper context passing:

from fastapi import BackgroundTasks, Depends
from auth import get_current_user_id

@app.post('/process-data')
async def process_data(
    data: dict,
    user_id: int = Depends(get_current_user_id),
    background_tasks: BackgroundTasks = None
):
    """Pass user context to background task"""
    background_tasks.add_task(
        process_in_background, 
        data, 
        user_id  # Pass security context
    )
    return {'status': 'processing'}

async def process_in_background(data: dict, user_id: int):
    """Background task with security context"""
    # Verify user still has permissions
    if not await user_has_permission(user_id, data):
        return
    await process_data_securely(data, user_id)

This pattern ensures background tasks maintain awareness of the original request's security context.

Frequently Asked Questions

How does Fastapi's automatic OpenAPI generation affect API security?
Fastapi's automatic OpenAPI generation is a double-edged sword. While it provides excellent documentation and client generation capabilities, it can inadvertently expose sensitive implementation details. The generated schema includes all Pydantic model fields by default, potentially revealing internal database structures, sensitive fields like password_hashes or internal IDs, and business logic that should remain hidden. middleBrick specifically analyzes OpenAPI schemas to identify these exposures and recommends using response_model_exclude/include parameters to control what data appears in the API contract.
Can Fastapi's dependency injection system introduce security vulnerabilities?
Yes, Fastapi's powerful dependency injection system can introduce security vulnerabilities when not used carefully. The system makes it easy to inject database sessions, authentication objects, or other resources without considering whether the endpoint should have access to them. A common pattern is injecting a database session and performing a simple get() operation without verifying the user's authorization to access that specific resource. middleBrick detects these patterns by analyzing the dependency chain and identifying endpoints that accept sensitive dependencies without proper access controls.