HIGH insecure direct object referencefastapicockroachdb

Insecure Direct Object Reference in Fastapi with Cockroachdb

Insecure Direct Object Reference in Fastapi with Cockroachdb — how this specific combination creates or exposes the vulnerability

Insecure Direct Object Reference (IDOR) occurs when an API exposes a reference to a resource—such as a numeric ID or UUID—in a predictable way and fails to enforce that the authenticated context has permission to access that specific resource. In Fastapi with Cockroachdb, this risk arises when route parameters like user_id or document_id are taken directly from the URL and used to build Cockroachdb SQL queries without verifying that the requesting user owns or is authorized to view that object.

Consider a Fastapi endpoint designed to retrieve a user profile by ID:

from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy import text
from database import get_session  # provides an async session to Cockroachdb

app = FastAPI()

@app.get("/users/{user_id}")
async def get_user(user_id: int, session=Depends(get_session)):
    result = session.execute(text("SELECT id, email, role FROM users WHERE id = :uid"), {"uid": user_id})
    row = result.fetchone()
    if row is None:
        raise HTTPException(status_code=404, detail="Not found")
    return {"id": row[0], "email": row[1], "role": row[2]}

If the API trusts the incoming user_id and uses it verbatim in a Cockroachdb SQL statement without confirming the requester’s authorization to that user ID, an attacker can change the ID (e.g., /users/123 to /users/124) and enumerate other users’ data. Cockroachdb returns the requested row if it exists, and the API leaks information across user boundaries. This becomes more impactful when sensitive fields like roles, permissions, or internal identifiers are exposed.

In a distributed SQL environment like Cockroachdb, developers sometimes assume that strong consistency and transactional guarantees reduce application-level authorization risks. However, the database enforces data constraints and isolation, not application-level authorization. An attacker can exploit predictable integer or sequential IDs to traverse relationships—for example, by probing /documents/1, /documents/2, etc.—and discover documents they should not see. If those documents contain regulated data, the exposure also increases compliance risk under frameworks such as OWASP API Top 10 (2023) A1: Broken Object Level Authorization, PCI-DSS, and SOC2.

Another common pattern is binding path parameters directly to object references used in Cockroachdb queries without considering multi-tenancy or tenant isolation. For instance, a workspace-based API might accept workspace_id and document_id but only validate the document existence in Cockroachdb, not whether the document belongs to the provided workspace or whether the caller has workspace access. Because Cockroachdb does not inherently enforce tenant boundaries at the SQL layer, the application must explicitly encode these checks. Missing tenant and ownership checks turn predictable object references into an IDOR vector.

IDOR also intersects with other checks performed by middleBrick, such as BOLA/IDOR and Property Authorization. Even if authentication is present, improper authorization logic allows one user to manipulate or view another user’s resources by tweaking object references. In Cockroachdb-driven Fastapi services, this often maps to missing row-level security policies or incomplete application-level authorization checks before constructing SQL statements.

Cockroachdb-Specific Remediation in Fastapi — concrete code fixes

Remediation centers on ensuring that every data access decision considers the requester’s identity and permissions, not just the existence of the requested object. Below are concrete, Cockroachdb-oriented fixes for Fastapi that you can apply immediately.

1. Include the requester’s ID in the query predicate

Always filter by the authenticated subject rather than trusting the client-supplied identifier alone. Combine the resource ID with the requester’s ID or tenant to form a compound lookup key.

from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy import text
from database import get_session, get_current_user  # get_current_user returns {user_id, role, tenant_id}

app = FastAPI()

@app.get("/users/{user_id}")
async def get_user(user_id: int, user=Depends(get_current_user), session=Depends(get_session)):
    result = session.execute(
        text("""
            SELECT id, email, role FROM users
            WHERE id = :uid AND id = :requester_id
            LIMIT 1
        """),
        {"uid": user_id, "requester_id": user.user_id}
    )
    row = result.fetchone()
    if row is None:
        raise HTTPException(status_code=403, detail="Forbidden or not found")
    return {"id": row[0], "email": row[1], "role": row[2]}

This ensures that even if an attacker changes user_id, the query will return no row unless the ID matches the authenticated user’s ID.

2. Enforce tenant or workspace ownership explicitly

For multi-tenant applications, include the tenant or workspace identifier in both the route or query parameters and the SQL predicate.

from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy import text
from database import get_session, get_current_user

app = FastAPI()

@app.get("/workspaces/{workspace_id}/documents/{document_id}")
async def get_document(
    workspace_id: int,
    document_id: int,
    user=Depends(get_current_user),
    session=Depends(get_session)
):
    result = session.execute(
        text("""
            SELECT id, title, content FROM documents
            WHERE id = :doc_id
              AND workspace_id = :workspace_id
              AND workspace_id IN (
                  SELECT workspace_id FROM workspace_members
                  WHERE user_id = :user_id
              )
            LIMIT 1
        """),
        {
            "doc_id": document_id,
            "workspace_id": workspace_id,
            "user_id": user.user_id
        }
    )
    row = result.fetchone()
    if row is None:
        raise HTTPException(status_code=403, detail="Document not accessible")
    return {"id": row[0], "title": row[1], "content": row[2]}

This pattern binds the document to a workspace and checks workspace membership on each request, preventing IDOR across tenants even when object IDs are guessable.

3. Use parameterized queries and avoid string interpolation

Never construct SQL by concatenating user input. Use bound parameters with Cockroachdb to prevent injection and ensure type safety. The examples above use text() with named placeholders, which is the recommended approach in Fastapi with SQLAlchemy.

4. Apply principle of least privilege to the Cockroachdb role used by Fastapi

Ensure the database user your Fastapi service connects with has only the permissions it needs—typically SELECT/INSERT/UPDATE/DELETE on specific tables, not superuser privileges. This limits the impact of any residual authorization bug.

5. Add logging and monitoring for suspicious access patterns

Log failed authorization attempts where a requested ID does not belong to the requester. Correlate these events across your Fastapi service and Cockroachdb audit logs to detect enumeration attacks early.

By combining route design discipline, explicit ownership checks in SQL, and least-privilege database credentials, you mitigate IDOR in Fastapi applications backed by Cockroachdb without relying on the database alone to enforce application-level permissions.

Related CWEs: bolaAuthorization

CWE IDNameSeverity
CWE-250Execution with Unnecessary Privileges HIGH
CWE-639Insecure Direct Object Reference CRITICAL
CWE-732Incorrect Permission Assignment HIGH

Frequently Asked Questions

Why can't I just rely on Cockroachdb row visibility or RBAC to prevent IDOR?
Cockroachdb provides strong consistency and access controls, but it does not enforce application-level ownership or tenant boundaries. IDOR is an application authorization issue: the API must verify that the authenticated subject is allowed to access the specific resource ID. Database roles can limit what SQL operations are allowed, but they cannot determine whether a user should see a particular row based on business logic. Always enforce authorization in Fastapi before constructing SQL queries.
Does using UUIDs instead of integer IDs fully prevent IDOR?
UUIDs reduce predictability of object references, making casual enumeration harder, but they do not prevent IDOR. If authorization checks are missing, a user can still access other valid UUIDs they should not see. Use UUIDs as opaque identifiers, but always pair them with proper ownership and tenant checks in Fastapi and in the corresponding Cockroachdb queries.