Prototype Pollution in Flask with Bearer Tokens
Prototype Pollution in Flask with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Prototype pollution in Flask applications that use Bearer token authentication can arise when user-controlled input modifies the properties of shared objects, often through JSON payloads that are merged into configuration or request-processing state. Bearer tokens are typically passed in the Authorization header, and while the token itself is not directly polluted, the request context that includes the token can be combined with vulnerable object-merging logic.
Consider a Flask route that merges incoming JSON into a global configuration or request context for authorization checks:
import json
from flask import Flask, request, g
app = Flask(__name__)
@app.before_request
def parse_token():
auth = request.headers.get('Authorization', '')
if auth.startswith('Bearer '):
g.token = auth[len('Bearer '):]
else:
g.token = None
@app.route('/update-config', methods=['POST'])
def update_config():
data = request.get_json()
# Vulnerable: merging user input into app config or g
for key, value in data.items():
setattr(g.config if hasattr(g, 'config') else type('Config', (), {})(), key, value)
return {'token_present': bool(g.token)}, 200
An attacker can send a JSON body like {"__proto__": {"isAdmin": true}} or use property pollution techniques (e.g., constructor.prototype.polluted = true) that affect how subsequent authorization checks interpret roles or permissions. If the application uses these merged properties to gate access, a polluted prototype can bypass Bearer token checks by altering inherited behavior or object equality checks. While Flask does not inherently rely on prototype chains as in JavaScript, pollution can still manifest in Python through class hierarchies, attribute resolution on objects stored in g, or when using libraries that perform deep merges.
The interaction with Bearer tokens becomes risky when token validation or scope derivation relies on object properties that can be influenced by user input. For example, if a token’s claims are merged into a mutable object and later compared or extended, polluted properties might change how claims are interpreted, potentially elevating privileges associated with the token. MiddleBrick’s checks for BOLA/IDOR and Property Authorization are designed to detect endpoints where object IDs or authorization-relevant properties are exposed to tampering, which can complement prototype pollution risks in token-bearing APIs.
Additionally, if the Flask app deserializes JSON into classes that use __setattr__ or metaclasses, attacker-supplied keys can trigger side effects during assignment, indirectly affecting token handling. Input validation and strict schema-based parsing reduce the likelihood of such interactions. MiddleBrick’s Input Validation and Property Authorization checks help surface endpoints where user data improperly influences object behavior, which is critical when Bearer tokens are used to enforce scope-based access.
Bearer Tokens-Specific Remediation in Flask — concrete code fixes
Remediation focuses on isolating token handling from user data and avoiding mutable merges of request input. Always parse the Bearer token in a dedicated, isolated context and validate incoming JSON against a strict schema before any use.
Secure token extraction and immutable usage:
from flask import Flask, request, g, jsonify
from jsonschema import validate, ValidationError
app = Flask(__name__)
TOKEN_SCHEMA = {
'type': 'object',
'properties': {
'resource': {'type': 'string'},
'scope': {'type': 'array', 'items': {'type': 'string'}}
},
'required': ['resource']
}
@app.before_request
def authenticate():
auth = request.headers.get('Authorization', '')
if not auth.startswith('Bearer '):
return jsonify({'error': 'Unauthorized'}), 401
token = auth[len('Bearer '):]
# Store only the raw token or a verified payload; avoid polluting shared objects
g.token = token
# Optionally decode and validate claims without merging into mutable shared state
# g.claims = safe_decode(token) # implement safe JWT decode as needed
@app.route('/data', methods=['POST'])
def handle_data():
data = request.get_json()
try:
validate(instance=data, schema=TOKEN_SCHEMA)
except ValidationError:
return jsonify({'error': 'Invalid input'}), 400
# Use data for business logic without merging into g.config or class prototypes
return jsonify({'received': True}), 200
Avoid setattr and use dataclasses or typed models:
from dataclasses import dataclass, asdict
from flask import Flask, request, jsonify
app = Flask(__name__)
@dataclass
class ResourceRequest:
resource: str
scope: list = None
@app.route('/submit', methods=['POST'])
def submit():
try:
body = request.get_json()
req = ResourceRequest(resource=body['resource'], scope=body.get('scope'))
except (KeyError, TypeError):
return jsonify({'error': 'Bad request'}), 400
# Work with req instance; no prototype or class-level pollution
return jsonify(asdict(req)), 200
Middleware isolation: Keep token-related state separate from request data. Do not iterate over user input to set attributes on objects used for authorization. If your application uses configuration objects, treat them as immutable during request processing. MiddleBrick’s CLI can be used to scan endpoints and verify that Bearer token handling does not intersect with mutable object merging, providing findings and remediation guidance via the middlebrick scan <url> command and the Web Dashboard to track improvements over time.