HIGH format stringflask

Format String in Flask

How Format String Manifests in Flask

Format string vulnerabilities in Flask applications arise when user-controlled input is used as a format string combined with a dictionary containing sensitive data. Python's % formatting operator allows accessing dictionary keys via %(key)s. If an attacker can control the format string, they may reference keys not intended for exposure, such as API keys, database credentials, or internal configuration values.

Consider this Flask route:

from flask import Flask, request

app = Flask(__name__)

# Sensitive configuration stored in a dictionary
CONFIG = {
    'api_key': 'sk_live_123456',
    'database_url': 'postgresql://user:pass@localhost/db'
}

@app.route('/welcome')
def welcome():
    # User controls 'template' parameter
    user_template = request.args.get('template', 'Welcome, %(username)s!')
    # Formatting with CONFIG dictionary
    return user_template % CONFIG

An attacker could call /welcome?template=%(api_key)s to retrieve the API key directly. Even if the dictionary is not explicitly passed, Flask's request.args or request.form are MultiDicts that support key access, making them vulnerable if used in formatting.

This falls under CWE-134 (Use of Externally-Controlled Format String) and can lead to severe information disclosure. In Flask APIs, such patterns often appear in dynamic message generation, log formatting, or response construction where developers mistakenly treat user input as a safe template.

Flask-Specific Detection

Detecting format string vulnerabilities in Flask requires identifying code paths where user input influences a format string used with a dictionary containing sensitive data. Manual review should search for:

  • Usage of % operator or str.format() where the left operand includes request.args, request.form, request.json, or other user-controlled sources.
  • Presence of a dictionary with sensitive keys (e.g., CONFIG, settings) that is used as the right operand in formatting.
  • Flask routes that return formatted strings directly without proper sanitization.

Dynamic scanning with middleBrick automates this detection. During an Input Validation scan, middleBrick sends payloads like %(api_key)s, %(secret)s, or %(password)s in query parameters, form data, and JSON bodies. If the API response contains values from the server's configuration dictionary, middleBrick flags a format string vulnerability. For example, a request to /welcome?template=%(api_key)s returning sk_live_123456 triggers a high-severity finding.

middleBrick's report includes the exact parameter and payload used, the leaked value, and maps the issue to CWE-134 and OWASP API Top 10:API4:2023 — Unrestricted Resource Consumption. The scan runs in seconds without credentials, making it suitable for testing unauthenticated endpoints.

You can run this scan via the Web Dashboard, the middlebrick scan <url> CLI, or integrate it into CI/CD with the GitHub Action to catch such issues before deployment.

Flask-Specific Remediation

Remediate format string vulnerabilities by eliminating the use of user-controlled format strings. In Flask, adopt these practices:

  1. Never use user input as a format template. Treat all user-supplied data as values, not as code or templates.
  2. Use f-strings or format() with sanitized values. Extract user input into variables and insert them into fixed string templates.
  3. For dynamic content, use Jinja2 templates with autoescaping. Flask's default templating engine safely escapes variables by default, preventing injection.

Here is a secure rewrite of the vulnerable example:

from flask import Flask, request, jsonify

app = Flask(__name__)

CONFIG = {
    'api_key': 'sk_live_123456',
    'database_url': 'postgresql://user:pass@localhost/db'
}

@app.route('/welcome')
def welcome():
    # Sanitize and use user input as value only
    username = request.args.get('username', 'Guest')
    # Fixed format string — no user control
    message = f"Welcome, {username}!"
    return jsonify({'message': message})

If custom templates are absolutely required (e.g., user-customizable emails), use a safe templating engine like Jinja2 with a restricted environment and never pass sensitive dictionaries as context. Instead, provide only the necessary user data:

from flask import render_template_string

@app.route('/custom')
def custom():
    user_template = request.args.get('template', 'Hello {{ name }}')
    # Render with limited context — no CONFIG
    return render_template_string(user_template, name='User')

Note: render_template_string still carries SSTI risks if the template is user-controlled; consider whitelisting allowed templates or using a sandboxed environment.

After fixing, verify with middleBrick's scan to ensure the format string vector is closed. The remediation guidance in middleBrick's report provides step-by-step instructions tailored to your Flask codebase.

Frequently Asked Questions

What is a format string vulnerability in Flask?
It occurs when user input is used as a format string (e.g., with Python's % operator) alongside a dictionary containing sensitive data. Attackers can then reference keys like 'api_key' to leak secrets, violating CWE-134.
How does middleBrick detect format string issues in Flask APIs?
middleBrick's Input Validation scan sends payloads such as %(api_key)s in request parameters. If the API response reveals values from the server's configuration dictionary, it flags a high-severity finding with proof and remediation steps.