Type Confusion in Fastapi with Api Keys
Type Confusion in Fastapi with Api Keys — how this specific combination creates or exposes the vulnerability
Type confusion in FastAPI when using API keys typically occurs when the framework or developer code treats the key’s data type inconsistently, such as accepting both a string and a number where only a string is valid. This mismatch can bypass intended validation checks and allow an attacker to substitute a numeric or structured value for a string key, potentially reaching endpoints they should not access.
FastAPI relies on Pydantic models and parameter declarations to validate incoming requests. When an API key is declared as a string in a header or query parameter but the application logic or schema also permits numeric types, FastAPI may coerce or silently convert the value. This coercion can lead to a type confusion scenario where an integer like 12345 is accepted where a string key such as abc-123 is expected. Because the key no longer matches the format used for authorization logic, it may map to a different internal representation or permission set.
Consider an endpoint that expects an API key in the header for authorization, but the validation layer does not enforce strict string typing. An attacker could send 12345 instead of a formatted key. If downstream code compares the received value against a list of valid keys stored as strings, the type mismatch may cause the check to behave unpredictably. In some cases, this can unintentionally grant access or leak information about which keys are valid through timing differences or error messages.
OpenAPI specifications generated by FastAPI often define API key parameters as strings with a in: header or in: query location. However, if the spec is not strictly validated at runtime or if the developer uses looser types in path operations, the contract between documentation and implementation can diverge. middleBrick scans such endpoints during a black-box assessment and flags inconsistencies between the declared type in the spec and the actual accepted input, highlighting where type confusion could be exploited.
Another angle involves query parameters that should be strings but are interpreted as integers by the framework due to automatic type conversion. For example, a parameter like ?key=123 may be read as an integer if the route function declares the argument as int. If the authorization logic later compares this integer to a set of string-based keys, the comparison may fail in ways that expose which keys are valid or bypass checks entirely. This pattern is relevant for unauthenticated attack surface testing, which middleBrick performs as part of its API security checks.
Insecure deserialization patterns or dynamic endpoint generation can compound type confusion risks. If FastAPI routes are constructed based on user input without strict type enforcement, an attacker may craft requests that traverse unexpected code paths. The scanner tests such scenarios by probing endpoints with varied input types and analyzing how the application responds, surfacing misconfigurations that could lead to privilege escalation or unauthorized access.
Api Keys-Specific Remediation in Fastapi — concrete code fixes
To prevent type confusion with API keys in FastAPI, enforce strict typing and explicit validation at the boundary of your application. Define API key parameters as strings in both route declarations and Pydantic models, and avoid implicit type conversions. The following examples demonstrate secure patterns using header-based and query-based API keys.
Header-based API key with strict string type
from fastapi import FastAPI, Header, HTTPException
app = FastAPI()
VALID_KEYS = {"abc-123", "def-456"}
@app.get("/secure")
async def secure_endpoint(x_api_key: str = Header(...)):
if x_api_key not in VALID_KEYS:
raise HTTPException(status_code=401, detail="Invalid API key")
return {"status": "ok"}
Query parameter API key with strict string type
from fastapi import FastAPI, Query, HTTPException
app = FastAPI()
VALID_KEYS = {"abc-123", "def-456"}
@app.get("/search")
async def search(
api_key: str = Query(..., description="API key for authentication")
):
if api_key not in VALID_KEYS:
raise HTTPException(status_code=401, detail="Invalid API key")
return {"results": []}
Pydantic model for structured API key validation
from fastapi import FastAPI, Depends
from pydantic import BaseModel, validator
from typing import ClassVar
app = FastAPI()
class ApiKeyPayload(BaseModel):
key: str
key_type: str = "public"
@validator("key")
def validate_key_format(cls, v):
if not v.startswith("ak_"):
raise ValueError("key must start with 'ak_'")
return v
VALID_KEYS: ClassVar[set[str]] = {"ak_abc123", "ak_def456"}
@app.post("/auth")
async def authenticate(payload: ApiKeyPayload):
if payload.key not in VALID_KEYS:
raise HTTPException(status_code=401, detail="Unauthorized")
return {"token": "example"}
Middleware enforcement to reject non-string keys
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
app = FastAPI()
@app.middleware("http")
async def enforce_string_api_key(request: Request, call_next):
api_key = request.headers.get("x-api-key")
if api_key is not None and not isinstance(api_key, str):
return JSONResponse(
status_code=400,
content={"detail": "API key must be a string"}
)
response = call_next(request)
return response
Using dependencies with explicit type checks
from fastapi import Depends, HTTPException, status
from typing import Annotated
def get_api_key(api_key: str = Header(...)) -> str:
if not isinstance(api_key, str):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="API key must be a string"
)
return api_key
ApiKeyDep = Annotated[str, Depends(get_api_key)]
@app.get("/items")
async def list_items(api_key: ApiKeyDep):
return {"data": "secure"}
These patterns ensure that API keys are consistently treated as strings, preventing type confusion. Combine this with automated security scans, such as those provided by middleBrick, to detect inconsistencies between your OpenAPI spec and runtime behavior. The CLI tool can be integrated into development workflows to validate configurations and surface risky parameter declarations before deployment.
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |