Denial Of Service in Fastapi with Cockroachdb
Denial Of Service in Fastapi with Cockroachdb — how this specific combination creates or exposes the vulnerability
A Denial of Service (DoS) risk in a Fastapi application using CockroachDB arises from resource contention and unbounded request patterns that can exhaust connection pools, thread limits, or database capacity. Fastapi is asynchronous and can accept many concurrent requests, but if handlers perform long-running or unbounded queries against CockroachDB without safeguards, the database can become a bottleneck.
With CockroachDB, DoS can manifest when:
- Unthrottled or high-concurrency requests open many CockroachDB connections or sessions, exhausting available connections on the cluster nodes.
- Long-running or inefficient SQL queries (e.g., missing indexes, full table scans, or cartesian products) block statement execution and increase latency, causing request pile-up in Fastapi.
- Lack of request-level timeouts or context cancellations means slow SQL queries hold HTTP worker threads and async event loop resources, degrading throughput.
- Absence of rate limiting or circuit-breaking allows bursts of traffic to overwhelm both Fastapi and CockroachDB, triggering retries that amplify load.
Because middleBrick scans the unauthenticated attack surface, it can detect missing timeouts, missing context propagation, missing statement timeouts, missing retries/backoff configuration, and absence of load testing that reveals DoS conditions. Findings typically map to OWASP API Top 10 #4 (Rate Limiting) and can correlate with SQL behavior that resembles SSRF or unsafe consumption when external services are involved.
Cockroachdb-Specific Remediation in Fastapi — concrete code fixes
Apply the following patterns in Fastapi to reduce DoS risk when interacting with CockroachDB. These examples use async drivers and context-aware timeouts to keep the system responsive under load.
1. Use async session and request-scoped dependencies with timeouts
Create a Fastapi dependency that opens a CockroachDB async session and ensures it is closed, while enforcing per-request timeouts:
from fastapi import Depends, FastAPI, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import sessionmaker, async_sessionmaker
import asyncio
import os
DATABASE_URL = os.getenv("COCKROACHDB_URL", "cockroachdb://root@localhost:26257/defaultdb?sslmode=require")
engine = create_async_engine(DATABASE_URL, pool_pre_ping=True, pool_size=10, max_overflow=5)
async_session_factory = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
async def get_db() -> AsyncSession:
async with async_session_factory() as session:
async with session:
# 5-second timeout for the whole dependency scope
try:
await asyncio.wait_for(session.execute(select(1)), timeout=5.0)
except asyncio.TimeoutError:
raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Database timeout")
yield session
2. Set CockroachDB statement timeouts and use context propagation
Ensure each query honors a deadline. Use SQL-level timeouts and propagate context across async calls:
import asyncio
from sqlalchemy import text
async def get_user_safe(db: AsyncSession, user_id: int, timeout: float = 3.0):
try:
result = await asyncio.wait_for(
db.execute(text("SELECT id, email FROM users WHERE id = :uid"), {"uid": user_id}),
timeout=timeout
)
return result.one_or_none()
except asyncio.TimeoutError:
raise HTTPException(status_code=status.HTTP_504_GATEWAY_TIMEOUT, detail="Query timeout")
3. Apply rate limiting and concurrency caps
Use Fastapi middleware or an external limiter to bound request rates and concurrent database sessions:
from fastapi.middleware import Middleware
from starlette.middleware.base import BaseHTTPMiddleware
import asyncio
class RateLimitMiddleware(BaseHTTPMiddleware):
def __init__(self, app, max_calls: int = 100, period: int = 60):
super().__init__(app)
self.max_calls = max_calls
self.period = period
self.calls = []
async def dispatch(self, request, call_next):
now = asyncio.get_event_loop().time()
self.calls = [t for t in self.calls if now - t < self.period]
if len(self.calls) >= self.max_calls:
raise HTTPException(status_code=429, detail="Rate limit exceeded")
self.calls.append(now)
response = await call_next(request)
return response
app = FastAPI(middleware=[Middleware(RateLimitMiddleware, max_calls=200, period=60)])
4. Use connection pool tuning and CockroachDB-specific settings
Configure pool sizes and statement timeouts to match your workload. For CockroachDB, prefer smaller pool sizes and lower timeouts to avoid overwhelming the cluster:
# Example connection string with statement timeout (synchronous mode for illustration)
# In async, set timeout per query as shown above
DATABASE_URL = "cockroachdb://root@localhost:26257/defaultdb?sslmode=require&connect_timeout=10&application_name=fastapi_app"
5. Graceful degradation and retry with backoff
Use exponential backoff for transient errors and fail fast for overload conditions:
import asyncio
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
import aiopg.exceptions
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=0.5, min=1, max=10),
retry=retry_if_exception_type((aiopg.exceptions.OperationalError, asyncio.TimeoutError)),
reraise=True
)
async def fetch_with_retry(db: AsyncSession, query):
try:
return await asyncio.wait_for(db.execute(query), timeout=4.0)
except (asyncio.TimeoutError, aiopg.exceptions.OperationalError):
raise
These patterns help ensure that Fastapi + CockroachDB deployments remain responsive under load and that DoS risks are surfaced during scans performed by tools like middleBrick.
Related CWEs: resourceConsumption
| CWE ID | Name | Severity |
|---|---|---|
| CWE-400 | Uncontrolled Resource Consumption | HIGH |
| CWE-770 | Allocation of Resources Without Limits | MEDIUM |
| CWE-799 | Improper Control of Interaction Frequency | MEDIUM |
| CWE-835 | Infinite Loop | HIGH |
| CWE-1050 | Excessive Platform Resource Consumption | MEDIUM |