Mass Assignment in Flask with Bearer Tokens
Mass Assignment in Flask with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Mass assignment in Flask applications occurs when a framework binds incoming request data directly to a model or object without explicit field filtering. When Bearer tokens are used for authentication, the risk pattern changes because developers sometimes conflate authentication with authorization and assume that a valid token alone is sufficient to trust request payloads. This assumption can lead to unsafe data binding, where an attacker authenticated with a Bearer token can submit additional fields to an endpoint and inadvertently modify attributes they should not access, such as is_admin, role, or permissions.
Consider a typical Flask route that deserializes JSON into a SQLAlchemy model using a pattern like user_data = request.get_json() followed by direct assignment or usage with update(user_data). If the client presents a Bearer token in the Authorization header, the server may treat the request as privileged and apply the payload without validating which fields are permitted. Because the token proves identity but not intent, mass assignment can allow privilege escalation or unintended data changes when the server binds all provided keys to the model.
In practice, this often maps to the BOLA/IDOR and BFLA/Privilege Escalation checks in middleBrick’s scan. The scanner tests endpoints protected by Bearer tokens by submitting extra fields (for example, role or is_admin) while maintaining a valid token, then checks whether those fields are applied to the backend logic. A positive finding indicates that authentication via Bearer token does not enforce a strict allowlist on input, which can lead to security weaknesses such as horizontal or vertical privilege escalation, depending on the data model.
Real-world analogies include scenarios where an API expects a profile update payload but receives additional attributes that change ownership or access level. Because OpenAPI/Swagger specs with full $ref resolution can describe which fields are expected, middleBrick cross-references the spec with runtime behavior to highlight mismatches between documented schema and actual mass assignment behavior, even when Bearer tokens are present.
Bearer Tokens-Specific Remediation in Flask — concrete code fixes
To mitigate mass assignment when using Bearer tokens in Flask, enforce explicit field allowlisting and never bind raw request JSON directly to models. Use a validation layer such as Marshmallow or a manual schema check to extract only permitted fields before updating any object. Require the Bearer token for protected routes but ensure authorization decisions are based on user roles and explicit field permissions rather than trusting the payload.
Below are concrete code examples demonstrating a secure approach.
Example 1: Manual field allowlist with Bearer token validation
from flask import Flask, request, jsonify
import jwt
app = Flask(__name__)
SECRET_KEY = 'your-jwt-secret'
def verify_bearer_token(token):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
return payload
except jwt.InvalidTokenError:
return None
@app.route('/users/<int:user_id>', methods=['PATCH'])
def update_user(user_id):
auth = request.headers.get('Authorization')
if not auth or not auth.startswith('Bearer '):
return jsonify({'error': 'Unauthorized'}), 401
token = auth.split(' ')[1]
user_payload = verify_bearer_token(token)
if not user_payload:
return jsonify({'error': 'Invalid token'}), 401
data = request.get_json()
# Explicit allowlist: only these fields can be updated
allowed_fields = {'display_name', 'email', 'bio'}
update_data = {k: v for k, v in data.items() if k in allowed_fields}
if not update_data:
return jsonify({'error': 'No valid fields to update'}), 400
# Here you would apply update_data to your data layer
# user_model.update(user_id, update_data)
return jsonify({'user_id': user_id, 'updated': update_data}), 200
Example 2: Using Marshmallow schema for strict deserialization
from flask import Flask, request, jsonify
from marshmallow import Schema, fields, ValidationError
import jwt
app = Flask(__name__)
SECRET_KEY = 'your-jwt-secret'
class UserUpdateSchema(Schema):
display_name = fields.String(required=False)
email = fields.Email(required=False)
bio = fields.String(required=False)
# Do not include is_admin or role here
def verify_bearer_token(token):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
return payload
except jwt.InvalidTokenError:
return None
@app.route('/users/<int:user_id>', methods=['PATCH'])
def update_user_schema(user_id):
auth = request.headers.get('Authorization')
if not auth or not auth.startswith('Bearer '):
return jsonify({'error': 'Unauthorized'}), 401
token = auth.split(' ')[1]
user_payload = verify_bearer_token(token)
if not user_payload:
return jsonify({'error': 'Invalid token'}), 401
schema = UserUpdateSchema()
try:
update_data = schema.load(request.get_json())
except ValidationError as err:
return jsonify({'errors': err.messages}), 400
# update_data contains only the fields defined in the schema
# user_model.update(user_id, update_data)
return jsonify({'user_id': user_id, 'updated': update_data}), 200
These examples ensure that even with a valid Bearer token, only explicitly permitted fields are considered for updates. Combine this with role-based checks on the server side to enforce that certain operations are restricted to specific roles. In a CI/CD context, the GitHub Action can be configured to fail builds if a scan detects mass assignment patterns on endpoints that require Bearer tokens, helping you catch regressions before deployment.
Related CWEs: propertyAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-915 | Mass Assignment | HIGH |