Broken Access Control in Fastapi with Cockroachdb
Broken Access Control in Fastapi with Cockroachdb — how this specific combination creates or exposes the vulnerability
Broken Access Control occurs when API endpoints fail to enforce proper authorization checks, allowing one user to access or modify another user’s resources. In a Fastapi application using Cockroachdb as the backend data store, this risk is amplified when route-level protections are incomplete or when developer assumptions about tenant isolation in a distributed SQL database prove incorrect.
Consider a typical pattern: a Fastapi route uses a decoded JWT payload to extract a user_id, then queries Cockroachdb with that identifier to retrieve or update a resource. If the route omits verifying that the requested resource (e.g., a document or profile) belongs to the same tenant or user, an attacker can manipulate the identifier (via BOLA/IDOR) to access another user’s data stored in Cockroachdb. Cockroachdb’s strong consistency and SQL semantics do not inherently enforce row-level ownership; they return rows when queries match the provided filters. A query like SELECT * FROM documents WHERE id = $1 without an additional tenant or user constraint becomes an implicit access bypass when the identifier is user-controlled.
Moreover, Fastapi dependency injection can inadvertently expose internal identifiers. If an endpoint exposes a database primary key in URLs (e.g., /documents/{doc_id}) and the query to Cockroachdb uses that key without validating ownership, horizontal privilege escalation occurs across users. In distributed deployments, Cockroachdb spans multiple nodes; developers might assume geo-partitioning or secondary indexes prevent cross-tenant reads, but misconfigured tables, missing row-level policies, or incomplete WHERE clauses can allow a single query to return records outside the intended scope. The unauthenticated attack surface of such endpoints depends on whether authentication is enforced before route execution and whether authorization checks occur after data retrieval.
OpenAPI/Swagger spec analysis can highlight these risks when paths lack securitySchemes or when the spec defines parameters that map directly to Cockroachdb identifiers without clarifying ownership constraints. Runtime findings from security checks like BOLA/IDOR and Property Authorization then surface gaps between documented behavior and actual database interactions, especially when routes assume implicit trust in the caller’s context.
Cockroachdb-Specific Remediation in Fastapi — concrete code fixes
To mitigate Broken Access Control with Cockroachdb in Fastapi, enforce strict ownership checks and parameterized queries that bind the authenticated subject to every database condition. Below are concrete, working examples that combine Fastapi security utilities with Cockroachdb drivers to ensure tenant and user boundaries are respected.
First, define a reusable dependency that extracts and validates the subject from the token, then use it in routes to construct safe queries. This ensures every Cockroachdb operation includes the user context required for authorization.
from fastapi import Depends, Fastapi, HTTPException, status
from pydantic import BaseModel
import asyncpg
import os
app = Fastapi()
# Assume a helper that validates JWT and returns subject
async def get_current_user() -> dict:
# In practice, decode and verify token; return minimal claims
return {"user_id": "usr-123", "tenant_id": "tenant-a"}
class Document(BaseModel):
id: str
content: str
@app.get("/documents/{doc_id}", response_model=Document)
async def read_document(
doc_id: str,
user: dict = Depends(get_current_user),
):
conn: asyncpg.Connection = None
try:
conn = await asyncpg.connect(os.getenv("COCKROACHDB_URL"))
row = await conn.fetchrow(
"SELECT id, content FROM documents WHERE id = $1 AND tenant_id = $2",
doc_id,
user["tenant_id"]
)
if row is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Not found or access denied")
return Document(id=row["id"], content=row["content"])
finally:
if conn:
await conn.close()
This pattern embeds tenant isolation directly into the SQL WHERE clause, preventing horizontal access across tenants stored in the same Cockroachdb cluster. For multi-tenant schemas, use row-level security (RLS) if available, but still enforce ownership in application code; RLS complements defense-in-depth but does not replace explicit checks in Fastapi routes.
When dealing with list endpoints, apply the same principle to avoid exposing collections that should be scoped to the subject:
@app.get("/documents", response_model=list[Document])
async def list_documents(
user: dict = Depends(get_current_user),
):
conn: asyncpg.Connection = None
try:
conn = await asyncpg.connect(os.getenv("COCKROACHDB_URL"))
rows = await conn.fetch(
"SELECT id, content FROM documents WHERE tenant_id = $1",
user["tenant_id"]
)
return [Document(id=r["id"], content=r["content"]) for r in rows]
finally:
if conn:
await conn.close()
Additionally, avoid exposing internal Cockroachdb primary keys directly in URLs when they can be enumerated; use opaque identifiers and maintain a mapping that is verified against tenant context. Combine these practices with Fastapi’s dependency injection to centralize authorization logic and reduce the likelihood of missing checks in individual routes.