Type Confusion in Fastapi with Jwt Tokens
Type Confusion in Fastapi with Jwt Tokens — how this specific combination creates or exposes the vulnerability
Type confusion in FastAPI when handling JWT tokens occurs when the framework or application code treats a token payload value as a specific type without validating or coercing it. Because FastAPI relies on Python type hints and automatic request parsing, a payload field that should be a string or integer can be deserialized into a different Python type if the client sends a mismatched JSON type. This can bypass expected authorization checks, cause runtime errors, or expose sensitive logic paths.
JWT tokens are typically validated and decoded into a Python dict or a Pydantic model. If the application declares a field as int but the client provides a string, FastAPI may still accept the input depending on how the endpoint is defined, leading to confusion between numeric IDs and token identifiers. For example, an endpoint expecting user_id: int might receive "1" or even a crafted token that places an array or object in that field. If the validation layer incorrectly trusts the decoded payload types, it may treat an array as an int, which can lead to insecure direct object references (IDOR) or privilege escalation when used in database queries or access control decisions.
In FastAPI, this often intersects with the BOLA/IDOR checks that are part of middleBrick’s 12 security scans. An attacker can supply a JWT with a manipulated payload where numeric identifiers are represented as strings or objects. If the server does not strictly enforce type checks after decoding, the token may be accepted, and the underlying object reference may be resolved incorrectly, exposing other users’ resources. The risk is compounded when the token is used to construct dynamic routes or queryset filters without additional authorization checks at the data layer.
middleBrick scans this attack surface by sending unauthenticated requests with malformed or unexpected token payload types and analyzing how the application responds. It checks whether the API correctly rejects mismatched types, whether error handling leaks information, and whether type confusion can be leveraged to bypass intended access controls. The scan correlates these runtime findings with OpenAPI/Swagger definitions, including $ref resolution, to verify that declared schemas align with actual behavior.
Jwt Tokens-Specific Remediation in Fastapi — concrete code fixes
Remediation focuses on strict type validation, explicit decoding, and defensive authorization checks. Always validate and cast payload fields after decoding, and do not rely solely on FastAPI’s automatic parameter conversion. Use Pydantic models with strict mode to enforce expected types for JWT claims, and apply additional checks before using identifiers in database queries.
Example 1: Strict Pydantic model for token payload
from fastapi import FastAPI, Depends, HTTPException, status
from pydantic import BaseModel, ValidationError
import jwt
app = FastAPI()
class TokenPayload(BaseModel):
sub: str
role: str
exp: int
def decode_token(token: str, secret: str) -> TokenPayload:
try:
raw = jwt.decode(token, secret, algorithms=["HS256"])
return TokenPayload(**raw)
except jwt.ExpiredSignatureError:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Token expired",
headers={"WWW-Authenticate": "Bearer"}
)
except ValidationError:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid token claims"
)
def get_current_user(token: str = Depends(oauth2_scheme), secret: str = "supersecretkey") -> TokenPayload:
return decode_token(token, secret)
@app.get("/users/me")
def read_current_user(current_user: TokenPayload = Depends(get_current_user)):
return {"user_id": current_user.sub, "role": current_user.role}
Example 2: Enforcing integer IDs with explicit casting and authorization
from fastapi import FastAPI, Depends, HTTPException, status, Query
from pydantic import BaseModel
import jwt
app = FastAPI()
class ResourceAccess(BaseModel):
user_id: int
def get_token_user(token: str = Depends(oauth2_scheme)):
# Assume decode and validation as above
payload = decode_token(token, "supersecretkey")
return payload
@app.get("/resources/{resource_id}")
def read_resource(
resource_id: int,
access: ResourceAccess = Depends(get_token_user),
query_id: int = Query(None)
):
# Ensure the requesting user is allowed to view this resource
if access.user_id != resource_id:
raise HTTPException(status_code=403, detail="Forbidden")
return {"resource_id": resource_id, "owner_id": access.user_id}
Additional practices
- Use strict=True in Pydantic models to prevent coercion from string to int and other unsafe conversions.
- Always re-check authorization on the server side; do not rely on the absence of type confusion to enforce permissions.
- Validate algorithm and issuer claims in the JWT to prevent token substitution attacks.
middleBrick’s LLM/AI Security checks can additionally probe endpoints that accept JWTs to ensure that prompt injection and jailbreak attempts cannot manipulate token handling or lead to output leakage.
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 |