Mass Assignment in Flask with Hmac Signatures
Mass Assignment in Flask with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Mass assignment in Flask occurs when an application directly passes user-controlled data structures (e.g., request JSON) to model constructors or update methods without explicit allowlisting of fields. When endpoints use Hmac signatures to validate request integrity, developers may assume the signature guarantees authenticity and therefore skip input filtering, inadvertently widening the attack surface. A typical pattern is verifying an Hmac signature on a request body and then using the full JSON payload to update a database record:
import hmac
import hashlib
from flask import Flask, request, jsonify
app = Flask(__name__)
SECRET = b'super-secret-key'
def verify_hmac(data, received_sig):
expected = hmac.new(SECRET, data, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, received_sig)
@app.route('/profile/', methods=['POST'])
def update_profile(user_id):
data = request.get_data()
sig = request.headers.get('X-Hmac-Signature')
if not verify_hmac(data, sig):
return jsonify({'error': 'invalid signature'}), 401
# Unsafe: directly using JSON to update user
user.update(request.json)
return jsonify({'status': 'ok'})
The signature ensures the request has not been tampered with in transit, but it does not limit which fields are applied to the model. An attacker who obtains or guesses a valid Hmac (for example, via a leaked secret or a compromised client) can craft requests that include unwanted properties such as is_admin, balance, or role. Because the endpoint uses the full JSON for mass update, those injected fields are applied to the user record, leading to privilege escalation or data manipulation. This pattern is common in APIs that adopt Hmac for request authentication while relying on framework conveniences like model.update(request.json) or Model.create(request.json). The risk is compounded when the Hmac is computed over the raw body, making replay of captured requests feasible if replay protections are absent. The combination of Hmac-based integrity checks and unchecked mass assignment creates a false sense of security where tampering is prevented but abuse of permissions is not.
Real-world analogies include the OWASP API Top 10 1:2023 – Broken Object Level Authorization, where missing property-level authorization intersects with authentication controls. For instance, an attacker may replay a legitimately signed request that updates profile fields, while also injecting additional fields that modify admin flags or financial values. In such scenarios, the signature does not mitigate BOLA or IDOR-style abuse because the server never validates whether the authenticated subject is allowed to modify the supplied fields. This highlights the need for explicit field allowlisting even when request integrity is cryptographically verified.
Hmac Signatures-Specific Remediation in Flask — concrete code fixes
To remediate mass assignment when using Hmac signatures in Flask, you must separate integrity verification from data binding. After confirming the Hmac, explicitly select only the fields the client is allowed to modify. Below are two concrete patterns: one for simple updates with an allowlist and one using a schema-based approach with marshmallow to enforce field-level control. The goal is to ensure that even a valid Hmac cannot bypass property authorization.
Pattern 1: Manual allowlist with Hmac verification
Define a set of writable fields and construct a filtered dictionary before applying updates. This keeps the logic transparent and avoids accidental exposure of sensitive attributes.
import hmac
import hashlib
from flask import Flask, request, jsonify
app = Flask(__name__)
SECRET = b'super-secret-key'
ALLOWED_PROFILE_FIELDS = {'display_name', 'email', 'bio'}
def verify_hmac(data, received_sig):
expected = hmac.new(SECRET, data, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, received_sig)
@app.route('/profile/', methods=['POST'])
def update_profile(user_id):
data = request.get_data()
sig = request.headers.get('X-Hmac-Signature')
if not verify_hmac(data, sig):
return jsonify({'error': 'invalid signature'}), 401
payload = request.get_json(force=True)
filtered = {k: v for k, v in payload.items() if k in ALLOWED_PROFILE_FIELDS}
# Assume user is fetched and updated safely
user = fetch_user(user_id)
for key, value in filtered.items():
setattr(user, key, value)
user.save()
return jsonify({'status': 'ok'})
Pattern 2: Schema-based validation with marshmallow
Using a schema enforces type and field constraints and makes the allowlist explicit. This pattern scales better when multiple endpoints share similar rules.
import hmac
import hashlib
from flask import Flask, request, jsonify
from marshmallow import Schema, fields, ValidationError
app = Flask(__name__)
SECRET = b'super-secret-key'
class ProfileSchema(Schema):
display_name = fields.String(required=False)
email = fields.Email(required=False)
bio = fields.String(required=False)
# Intentionally omitting is_admin, role, balance, etc.
def verify_hmac(data, received_sig):
expected = hmac.new(SECRET, data, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, received_sig)
@app.route('/profile/', methods=['POST'])
def update_profile_schema(user_id):
data = request.get_data()
sig = request.headers.get('X-Hmac-Signature')
if not verify_hmac(data, sig):
return jsonify({'error': 'invalid signature'}), 401
schema = ProfileSchema()
try:
params = schema.load(request.get_json(force=True))
except ValidationError as err:
return jsonify({'errors': err.messages}), 400
user = fetch_user(user_id)
for key, value in params.items():
setattr(user, key, value)
user.save()
return jsonify({'status': 'ok'})
These patterns ensure that Hmac signatures validate the request origin while the application retains strict control over which properties can be modified. They also align with the principle of defense in depth: integrity checks plus explicit authorization of data fields. Note that Hmac secrets must be managed securely outside the application code, and rotated periodically to reduce exposure from potential leaks.
Related CWEs: propertyAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-915 | Mass Assignment | HIGH |