Time Of Check Time Of Use in Fastapi with Api Keys
Time Of Check Time Of Use in Fastapi with Api Keys — how this specific combination creates or exposes the vulnerability
Time Of Check Time Of Use (TOCTOU) is a class of race condition where the state of a resource changes between a security check and the subsequent use of that resource. In FastAPI applications that rely on API keys for authorization, TOCTOU emerges when the key is validated early in the request lifecycle—often in a dependency or middleware—and the authorization decision is reused later in the route handler without re-verifying permissions or state.
Consider a FastAPI service that authenticates requests via an API key header and then performs per-user or per-resource authorization checks inside the handler. If the API key is checked once via a dependency that loads user permissions into the request state, but the handler subsequently accesses or modifies a resource based on parameters derived from the request (such as an identifier in the path or body), an attacker can exploit timing or logical gaps to operate on a different resource than the one validated. For example, a dependency might verify that the key belongs to a user and attach user_permissions to the request state, but the handler may call fetch_document(document_id) where document_id is user-supplied. If the handler does not re-validate that the authenticated key grants access to that specific document_id, the earlier check becomes a stale decision that can be bypassed via crafted concurrent requests or parameter manipulation.
With API keys, which are often bearer tokens treated as static credentials, the risk is compounded when authorization is inferred from cached or coarse-grained data. A key might be valid and broadly scoped in the authentication layer, but the application must enforce fine-grained authorization at the resource level. If the check occurs before the resource identifier is fully resolved or trusted, and the use occurs after—especially when combined with asynchronous execution or cached permissions—an attacker can race conditions or parameter substitution to make an unauthorized call appear authorized. This pattern is particularly dangerous when rate limiting, bulk operations, or background tasks derive decisions from the same cached state without fresh validation at the point of use.
In practical terms, TOCTOU in this context does not require a sophisticated distributed attack; it can be triggered by sending carefully timed or concurrent requests that manipulate identifiers while relying on a stale authorization decision. Because API keys are often used for simplicity and performance, developers may assume that a single check is sufficient, inadvertently creating a window where the system’s security posture diverges from its intended design. Tools like middleBrick that test unauthenticated attack surfaces and include checks for BOLA/IDOR and authorization consistency are valuable for detecting such mismatches between authentication and fine-grained authorization in FastAPI services.
Api Keys-Specific Remediation in Fastapi — concrete code fixes
To mitigate TOCTOU when using API keys in FastAPI, re-validate authorization at the point of use and avoid relying on cached or coarse-grained permissions across the request lifecycle. Below are concrete, working examples that demonstrate secure patterns.
Example 1: Re-validate resource ownership inside the handler
from fastapi import FastAPI, Depends, HTTPException, Security, Header
from pydantic import BaseModel
from typing import Dict
app = FastAPI()
# Simulated key-to-permissions store
API_KEY_PERMISSIONS: Dict[str, Dict[str, set]] = {
"trusted-key-123": {"documents": {"read": {"doc-1", "doc-2"}, "write": {"doc-1"}}},
}
class Document(BaseModel):
id: str
content: str
def get_current_key(x_api_key: str = Header(...)) -> str:
if x_api_key not in API_KEY_PERMISSIONS:
raise HTTPException(status_code=401, detail="Invalid API key")
return x_api_key
@app.get("/documents/{doc_id}")
def read_document(doc_id: str, api_key: str = Security(get_current_key)):
# Re-validate at use time: ensure the key permits read on this specific doc_id
perms = API_KEY_PERMISSIONS.get(api_key, {}).get("documents", {}).get("read", set())
if doc_id not in perms:
raise HTTPException(status_code=403, detail="Access denied to this document")
# Proceed with safe operation
return Document(id=doc_id, content="Sensitive data")
Example 2: Centralize authorization checks with a dependency that returns a guard function
from fastapi import FastAPI, Depends, Security, Header, HTTPException
from typing import Callable
app = FastAPI()
API_KEY_SCOPES: Dict[str, Dict[str, set]] = {
"key-a": {"organizations": {"org-100": {"read", "write"}}},
"key-b": {"organizations": {"org-200": {"read"}}},
}
def require_resource_access(resource_type: str, required_action: str):
def inner(x_api_key: str = Header(...)):
if x_api_key not in API_KEY_SCOPES:
raise HTTPException(status_code=401, detail="Invalid API key")
scopes = API_KEY_SCOPES[x_api_key].get(resource_type, {})
# Simulate resolving target resource from request state or path
# In practice, you would resolve the resource_id safely and check membership
if required_action not in scopes.get("org-100", set()):
raise HTTPException(status_code=403, detail="Insufficient scope")
return x_api_key
return inner
@app.delete("/organizations/{org_id}/members")
def delete_member(org_id: str, _: str = Security(require_resource_access("organizations", "write"))):
# Only reachable if the key has write access to the specific org
return {"status": "deleted"}
General guidance
- Treat API keys as coarse credentials; always enforce fine-grained checks immediately before data access or modification.
- Avoid storing authorization decisions in request state that persist across multiple handler calls without re-verifying inputs.
- Design handlers to be idempotent and safe by validating inputs and permissions independently on each invocation.
These patterns reduce the window where a check and use can diverge, addressing the TOCTOU risk specific to API-key-based FastAPI services. For broader coverage, including detection of BOLA/IDOR and permission inconsistencies, middleBrick’s scans can help surface these classes of issues during testing.