Format String in Fastapi
How Format String Manifests in Fastapi
Format string vulnerabilities in Fastapi applications typically occur when user input is directly passed to Python's string formatting operations without proper sanitization. While Fastapi itself provides strong typing and validation through Pydantic models, developers can inadvertently introduce format string vulnerabilities through several common patterns.
The most frequent manifestation occurs in logging statements where request parameters are interpolated directly into log messages. Consider this Fastapi endpoint:
@app.get("/user")
def get_user_info(user_id: str = Depends(get_user_id)):
logger.info(f"User {user_id} accessed profile")
return user_service.get_user(user_id)This appears safe, but if user_id contains format specifiers like {name}, an attacker could manipulate the logging output. More dangerously, format string vulnerabilities appear when constructing database queries or file paths:
@app.get("/logs/{filename}")
def get_log_file(filename: str):
# VULNERABLE: filename could contain format specifiers
content = open(f"/var/logs/{filename}").read()
return contentFastapi's path parameter binding doesn't automatically sanitize format specifiers, so filename could contain file.txt{user_input} which might cause unexpected behavior in certain contexts.
Another Fastapi-specific scenario involves template rendering with user-controlled data. When using Jinja2 templates (Fastapi's default), format string issues can arise if template variables contain unescaped format specifiers:
@app.get("/report/{report_id}")
def generate_report(report_id: str):
report_data = report_service.get_report(report_id)
# If report_data contains { or } characters, template rendering issues occur
return templates.TemplateResponse("report.html", {"report": report_data})The vulnerability becomes critical when format strings are used in security-sensitive contexts like JWT claims, authorization headers, or API key generation where an attacker could manipulate the format to bypass security controls.
Fastapi-Specific Detection
Detecting format string vulnerabilities in Fastapi requires both static analysis and dynamic testing. Static analysis tools can identify dangerous patterns where user input flows into string formatting operations. However, Fastapi's dynamic nature with dependency injection and path parameters makes comprehensive static analysis challenging.
middleBrick's scanner specifically targets format string vulnerabilities in Fastapi applications through black-box testing. The scanner automatically tests endpoints with format string payloads to identify potential vulnerabilities. Here's how middleBrick detects these issues:
# Scan a Fastapi application with middleBrick
middlebrick scan https://api.example.com --profile fastapiThe scanner tests for format string injection by sending payloads containing format specifiers like %s, %x, {0}, and {name} to all parameters. For Fastapi applications, it specifically tests:
- Path parameters:
/users/{user_id}with{user_id}=%s - Query parameters:
/search?q={query}with{query}={name} - Request body fields: JSON payloads with format specifiers
middleBrick's LLM/AI security module also checks for format string vulnerabilities in AI endpoints, testing for prompt injection through format string manipulation in system prompts and user messages.
Developers can integrate format string detection into their CI/CD pipeline using the middleBrick GitHub Action:
- name: Run middleBrick Security Scan
uses: middlebrick/middlebrick-action@v1
with:
target_url: http://localhost:8000
fail_on_severity: high
profile: fastapiThis automatically fails builds when format string vulnerabilities are detected, preventing vulnerable code from reaching production.
Fastapi-Specific Remediation
Remediating format string vulnerabilities in Fastapi requires a defense-in-depth approach. The primary strategy is to avoid string interpolation with user input entirely. Instead, use parameterized operations and explicit formatting:
# VULNERABLE - direct interpolation
@app.get("/logs/{filename}")
def get_log_file(filename: str):
content = open(f"/var/logs/{filename}").read() # Vulnerable
return contentRemediation:
# SECURE - explicit validation and safe operations
@app.get("/logs/{filename}")
def get_log_file(filename: str):
# Validate filename - only allow alphanumeric and specific extensions
if not re.match(r'^[a-zA-Z0-9_-]+\.log$', filename):
raise HTTPException(status_code=400, detail="Invalid filename")
# Use pathlib for safe path operations
log_path = Path("/var/logs") / filename
if not log_path.is_file():
raise HTTPException(status_code=404, detail="Log not found")
return log_path.read_text()For logging, use structured logging instead of string interpolation:
# VULNERABLE
logger.info(f"User {user_id} accessed profile at {timestamp}")Remediation:
# SECURE - structured logging
logger.info("User accessed profile", user_id=user_id, timestamp=timestamp)When format strings are necessary, use explicit format specifiers with constant format strings:
# SECURE - constant format string
message_template = "User {user_id} accessed profile"
message = message_template.format(user_id=user_id)middleBrick's remediation guidance includes specific recommendations for Fastapi applications, such as using Pydantic models for input validation and Fastapi's built-in dependency injection to sanitize user input before it reaches business logic.