Injection Flaws in Fastapi with Cockroachdb
Injection Flaws in Fastapi with Cockroachdb — how this specific combination creates or exposes the vulnerability
Injection flaws occur when untrusted data is interpreted as part of a command or query. In a Fastapi service that uses Cockroachdb, the risk typically arises when SQL strings are constructed by concatenating or interpolating user-controlled input into queries sent to the database. Cockroachdb is PostgreSQL wire-compatible, so standard SQL injection techniques that affect PostgreSQL also apply. Fastapi encourages the use of dependency injection and modern async patterns, but if developers pass raw request parameters directly into string-based queries, the database backend can execute unintended SQL.
An example vulnerability pattern is building a query with Python string formatting or concatenation:
query = f"SELECT * FROM accounts WHERE user_id = '{user_id}'"
If user_id contains SQL fragments, Cockroachdb will attempt to execute them. Because Cockroachdb supports advanced SQL features, attackers may exploit UNION-based injection, crafted boolean conditions, or time-based blind techniques to infer data. Injection can also surface through ORM misconfiguration; even when using an async ORM like SQLAlchemy with Cockroachdb, dynamically composing filter expressions from unchecked input can result in unsafe queries. Another subtle vector is JSONB extraction paths in Cockroachdb: if user input is used to navigate JSON structures without validation, injection can manipulate the path traversal to access or modify unintended document segments.
In Fastapi, endpoints that expose search or lookup functionality are commonly at risk. For instance, a route that accepts category and sort query parameters and directly embeds them into a SQL string will expose the attack surface. The stateless, unauthenticated scan approach of middleBrick can detect such endpoints by observing behavior differences when injection payloads are supplied, such as error-based deviations or inconsistent timing. Injection flaws pair with Cockroachdb’s wire compatibility by allowing payloads that use PostgreSQL-specific syntax, making detection and exploitation possible across a wide set of techniques described in the OWASP API Top 10 and mapped to compliance frameworks like PCI-DSS and SOC2.
Cockroachdb-Specific Remediation in Fastapi — concrete code fixes
To eliminate injection risks when using Cockroachdb in Fastapi, always parameterize queries and avoid dynamic SQL assembly. Use prepared statements or an async ORM with strict input validation. Below are concrete, safe patterns.
1. Parameterized queries with asyncpg
Direct interaction with Cockroachdb via the PostgreSQL wire protocol using asyncpg is common. Use placeholders and pass parameters separately:
import asyncio
import asyncpg
from fastapi import FastAPI, Query
app = FastAPI()
async def get_account(user_id: str):
conn = await asyncpg.connect(
host='DB_HOST',
port=26257,
user='DB_USER',
password='DB_PASSWORD',
database='DB_NAME',
)
try:
# Safe: parameters are passed separately, not interpolated
row = await conn.fetchrow('SELECT id, name FROM accounts WHERE id = $1', user_id)
return dict(row) if row else None
finally:
await conn.close()
@app.get('/accounts')
async def read_account(q: str = Query(..., min_length=1)):
return await get_account(q)
Notice the placeholder $1 and the separate argument in fetchrow; Cockroachdb will treat the supplied value strictly as data.
2. SQLAlchemy async with bound parameters
When using SQLAlchemy with Cockroachdb, construct queries using the ORM or Core with bound parameters:
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker, declarative_base
from sqlalchemy import select, Table, MetaData, Column, String
DATABASE_URL = "cockroachdb://user:password@host:26257/dbname?sslmode=require"
engine = create_async_engine(DATABASE_URL, echo=False)
AsyncSessionLocal = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
Base = declarative_base()
class Account(Base):
__tablename__ = 'accounts'
id = Column(String, primary_key=True)
name = Column(String)
async def get_account_safe(session: AsyncSession, user_id: str):
stmt = select(Account).filter(Account.id == user_id)
result = await session.execute(stmt)
return result.scalar_one_or_none()
@app.get('/accounts/orm')
async def read_account_orm(q: str = Query(..., min_length=1)):
async async_session(AsyncSessionLocal) as session:
account = await get_account_safe(session, q)
return {'id': account.id, 'name': account.name} if account else None
The ORM generates parameterized SQL under the hood; never use text() with string interpolation unless you bind parameters explicitly via .params().
3. Input validation and allowlists
Complement parameterization with strict validation. For identifiers, use length checks and allowlists:
from fastapi import HTTPException
from pydantic import constr
UserId = constr(min_length=1, max_length=64, regex=r'^[A-Za-z0-9_-]+$')
@app.get('/validate')
async def validated_lookup(uid: UserId):
# Safe to use in parameterized context
return await get_account(uid)
By combining parameterized queries, an async driver or ORM, and strict input validation, you mitigate injection risks while retaining Cockroachdb’s capabilities.