Format String in Flask with Bearer Tokens
Format String in Flask with Bearer Tokens — how this specific combination creates or exposes the vulnerability
A format string vulnerability occurs when user-controlled input is passed directly to a formatting function such as logging.info, str.format, or percent-style formatting without proper sanitization. In Flask APIs that use Bearer tokens for authentication, the token value itself can become a vector if it is reflected in logs, error messages, or debugging output and later used in an insecure format string operation.
Consider a Flask route that authenticates via a Bearer token and then includes the token or a derived value in a logging call:
from flask import Flask, request
import logging
app = Flask(__name__)
@app.route('/api/data')
def get_data():
auth = request.headers.get('Authorization', '')
if not auth.startswith('Bearer '):
return {'error': 'Unauthorized'}, 401
token = auth.split(' ')[1]
# Risk: token used directly in logging with format string
logging.info('Authenticated token: %s' % token)
return {'status': 'ok'}
If an attacker sends a crafted Bearer token containing format specifiers (e.g., %s%s%s%s), the logging call may read or leak stack memory, potentially exposing sensitive data or causing denial of service. More critically, if the token is later interpolated into responses or stored and then rendered via an insecure template or logging pipeline, it can lead to information disclosure or secondary injection issues.
The risk is compounded when developers assume that token handling is safe simply because it comes from an Authorization header. Bearer tokens are high-value targets; leaking them via format strings can lead to credential compromise. In addition, format strings in error handlers or debug endpoints may echo the token back to the client, turning what should be an opaque credential into a reflected attack surface.
Another scenario involves using the token in structured logging or metrics where format strings are built dynamically:
logger = logging.getLogger('api')
# Risk: token passed as format argument without sanitization
logger.info('Token info: %s, %d', token, len(token))
Although this usage is safer than percent formatting, if the logging framework or surrounding code later reuses these values in an unsafe manner, the exposure window remains. The key takeaway is that Bearer tokens must be treated as sensitive data at all times and must never be concatenated or interpolated using uncontrolled format strings.
Bearer Tokens-Specific Remediation in Flask — concrete code fixes
To mitigate format string risks when working with Bearer tokens in Flask, ensure tokens are handled as opaque strings and never used as format arguments or in dynamic string construction. Use safe logging practices and strict input validation.
1. Avoid formatting tokens into log messages. Instead, log metadata that does not expose the token:
from flask import Flask, request
import logging
app = Flask(__name__)
@app.route('/api/data')
def get_data():
auth = request.headers.get('Authorization', '')
if not auth.startswith('Bearer '):
return {'error': 'Unauthorized'}, 401
token = auth.split(' ')[1]
# Safe: log only presence, not the token itself
logging.info('Authenticated request with Bearer token present')
return {'status': 'ok'}
2. If you must record token identifiers for debugging, hash or truncate them rather than logging the raw value:
import hashlib
def safe_token_id(token: str) -> str:
return hashlib.sha256(token.encode()).hexdigest()[:16]
@app.route('/api/data')
def get_data():
auth = request.headers.get('Authorization', '')
if not auth.startswith('Bearer '):
return {'error': 'Unauthorized'}, 401
token = auth.split(' ')[1]
logging.info('Authenticated token ID: %s', safe_token_id(token))
return {'status': 'ok'}
3. Ensure error handlers do not echo tokens. Validate and sanitize any data included in error responses:
from flask import jsonify
@app.errorhandler(500)
def handle_500(e):
# Avoid including request headers or tokens in error output
return jsonify({'error': 'Internal server error'}), 500
4. Use Flask’s built-in mechanisms for secure configuration and avoid string interpolation when constructing messages that may be formatted later:
import logging
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# Safe: formatter does not include token
handler = logging.StreamHandler()
handler.setFormatter(formatter)
logger = logging.getLogger('api')
logger.addHandler(handler)
These practices reduce the chance that Bearer tokens are exposed through format string misuse while maintaining useful observability.