Format String in Fastapi with Jwt Tokens
Format String in Fastapi with Jwt Tokens — how this specific combination creates or exposes the vulnerability
A format string vulnerability occurs when user-controlled input is passed directly into a formatted output function without proper sanitization. In FastAPI, this risk can intersect with JWT token handling when developer code uses unchecked request data to construct log entries, error messages, or debugging output that includes token values or claims. For example, if a route receives a username or a token identifier via query or body parameters and then uses Python’s percent-style or str.format syntax to build a message, an attacker can supply format specifiers such as %s, %x, or %n to read stack memory or cause unintended behavior. Because JWT tokens are often long strings containing sensitive metadata, exposing them through formatted output can leak information that assists further attacks.
Consider a FastAPI endpoint that logs authentication attempts by embedding the token directly into a format string:
from fastapi import FastAPI, Header
app = FastAPI()
@app.get("/items")
def read_items(authorization: str = Header(None)):
message = "Auth token: %s" % authorization # Risk if authorization is attacker-controlled
print(message)
return {"status": "ok"}
If an attacker sends authorization as %s %s %s, the application may leak memory contents or raise exceptions that reveal internal state. Similarly, using str.format with user input poses the same class of risk:
log_msg = "Token submitted: {}".format(user_token)
In both cases, the JWT token value or surrounding context can be manipulated through format specifiers, especially when the developer assumes the input is a simple opaque string. Because FastAPI applications often integrate with security middleware that handles JWT validation, accidental exposure through logging or error formatting creates an information disclosure vector. Attackers can chain this with other weaknesses, such as missing rate limiting or insecure error handling, to perform reconnaissance or refine injection attempts. The vulnerability is not in JWT parsing itself, but in how token-related data is handled post-validation.
Jwt Tokens-Specific Remediation in Fastapi — concrete code fixes
To mitigate format string risks when working with JWT tokens in FastAPI, ensure that any user-influenced data used in string construction is treated as plain values, not format templates. Prefer explicit concatenation or secure logging utilities that do not interpret format specifiers. Below are concrete, safe patterns for handling JWT tokens in request headers and responses.
Remediation example 1: Safe logging with f-strings or explicit concatenation
Instead of using percent or str.format interpolation, use f-strings where the token is inserted as a value, not as a format directive:
from fastapi import FastAPI, Header
app = FastAPI()
@app.get("/items")
def read_items(authorization: str = Header(None)):
# Safe: token is a value, not a format string
message = f"Auth token: {authorization}"
print(message)
return {"status": "ok"}
Remediation example 2: Structured logging with dictionaries
Use structured logging libraries (e.g., Python’s built-in logging with dict-like messages or a dedicated logger) to avoid interpreting input as format directives:
import logging
from fastapi import FastAPI, Header
app = FastAPI()
logger = logging.getLogger("security")
@app.get("/items")
def read_items(authorization: str = Header(None)):
logger.info("Auth token received", extra={"token": authorization})
return {"status": "ok"}
Remediation example 3: Input validation and length limits
Validate JWT tokens to ensure they conform to expected patterns (e.g., base64url segments separated by dots) and enforce length limits to reduce the impact of accidental exposure:
from fastapi import FastAPI, Header, HTTPException import re app = FastAPI() JWT_PATTERN = re.compile(r'^[A-Za-z0-9._\-]+\.[A-Za-z0-9._\-]+\.[A-Za-z0-9._\-]+$') @app.get("/items") def read_items(authorization: str = Header(None)): if not authorization or not JWT_PATTERN.match(authorization): raise HTTPException(status_code=400, detail="Invalid token format") # Safe usage after validation return {"token_valid": True}Remediation example 4: Avoid embedding tokens in error messages
Ensure that exception handlers do not include raw token values in responses or logs. Use generic error messages and log token metadata separately with strict access controls:
from fastapi import FastAPI, Request from fastapi.responses import JSONResponse app = FastAPI() @app.exception_handler(Exception) async def global_exception_handler(request: Request, exc: Exception): # Do not include token or user input in public response return JSONResponse( status_code=500, content={"error": "Internal server error"} )