Format String in Fastapi with Api Keys
Format String in Fastapi with Api Keys — how this combination creates or exposes the vulnerability
A Format String vulnerability occurs when user-controlled input is passed directly into a formatting function such as str.format, , or logging string interpolation without proper sanitization. In FastAPI, this risk is amplified when API keys are handled carelessly—for example, logging an incoming key with a format placeholder or echoing it in a response message that includes unchecked user input.
Consider a FastAPI endpoint that receives an API key in a header and logs it using an f-string combined with external data:
from fastapi import FastAPI, Header, HTTPException
import logging
logging.basicConfig(level=logging.INFO)
app = FastAPI()
@app.get("/items/")
async def read_items(x_api_key: str = Header(...)):
user_supplied = "unknown"
# UNSAFE: user_supplied is used inside an f-string with the API key
logging.info(f"Received API key {x_api_key} for user {user_supplied}")
return {"key": x_api_key, "user": user_supplied}
If an attacker sends a crafted header such as X-Api-Key: %s%s%s%s, the log output can expand to consume memory or leak surrounding memory contents, potentially exposing other secrets or causing denial of service. More broadly, format strings can reveal whether an API key is being processed, its length, and structure through log timing or error messages, aiding reconnaissance for further attacks.
The same risk appears when API keys are reflected in HTTP responses or error messages using insecure formatting, for instance:
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/echo/")
async def echo(key: str = Query(...)):
# UNSAFE: key is interpolated directly into a user-facing string
return {"message": "Your key is {}".format(key)}
Although this example may seem benign, if the key contains format specifiers (e.g., {user_id}), Python’s str.format can interpret them, leading to unintended substitutions or information disclosure. In the context of API security checks run by middleBrick, such patterns are flagged because they expose API keys through logging or output formatting, violating data exposure controls.
In broader assessments, format string issues intersect with authentication and data exposure checks. middleBrick’s scans test unauthenticated endpoints and can surface these patterns when OpenAPI specs or runtime behavior reveal insecure string handling of credentials. Remediation focuses on avoiding direct interpolation of secrets and never reflecting raw keys in responses or logs.
Api Keys-Specific Remediation in Fastapi — concrete code fixes
Secure handling of API keys in FastAPI requires keeping keys out of logs, avoiding dynamic formatting with user input, and ensuring keys are treated as opaque strings. Below are concrete, safe patterns.
1. Use structured logging without interpolating secrets
Log metadata separately from the API key value. Never include the key inside a formatted string that also includes user-controlled fields.
import logging
from fastapi import FastAPI, Header
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = FastAPI()
@app.get("/items/")
async def read_items(x_api_key: str = Header(...)):
# SAFE: log a hash or a fixed message, not the key itself
logger.info("API key received", extra={"key_hash": hash(x_api_key)})
return {"status": "ok"}
2. Avoid reflection of raw keys in responses
Do not return the API key in JSON or error messages. If you must acknowledge receipt, use a constant, non-sensitive response.
from fastapi import FastAPI, Header
app = FastAPI()
@app.get("/validate/")
async def validate_key(x_api_key: str = Header(...)):
# SAFE: do not echo the key
return {"valid": True}
3. Use environment variables and secure configuration
Load API keys from environment variables and access them via settings, ensuring they are never constructed from user input.
import os
from fastapi import FastAPI
app = FastAPI()
API_KEY = os.getenv("SERVICE_API_KEY")
@app.get("/check/")
async def check_key(x_api_key: str = Header(...)):
if x_api_key != API_KEY:
raise HTTPException(status_code=403, detail="Invalid key")
return {"result": "authorized"}
4. Disable unnecessary debug output in production
Ensure logging levels and formats do not inadvertently print raw headers. Configure loggers explicitly to exclude sensitive fields.
import logging
from fastapi import FastAPI, Header
# Configure a filter that redacts sensitive header names in logs
class RedactFilter(logging.Filter):
def filter(self, record):
if "x_api_key" in record.getMessage():
return False
return True
logger = logging.getLogger(__name__)
logger.addFilter(RedactFilter())
app = FastAPI()
@app.get("/items/")
async def read_items(x_api_key: str = Header(...)):
logger.info("Request processed")
return {"status": "ok"}
By following these practices, you reduce the risk of format string–related information leakage involving API keys. middleBrick’s checks for Authentication and Data Exposure will highlight insecure logging or reflection patterns, guiding you toward safer credential handling in FastAPI services.