Weakness reference
CWE-915

Improperly Controlled Modification of Dynamically-Determined Object Attributes

Mass assignment is a vulnerability where an application automatically binds user-supplied input directly to object properties without restricting which…

01Summary

Mass assignment is a vulnerability where an application automatically binds user-supplied input directly to object properties without restricting which attributes can be modified. An attacker can exploit this to set sensitive fields—such as user roles, permissions, or internal flags—that were never intended to be user-controllable, leading to privilege escalation or data tampering.

02How It Happens

Many modern frameworks offer convenience features that automatically map incoming request parameters (query strings, form data, JSON) to object attributes. While this speeds up development, it becomes dangerous when the application trusts all incoming data without an allowlist of safe, modifiable fields. An attacker can simply add extra parameters to a request targeting attributes the developer never expected to be changed—such as is_admin, role, account_balance, or internal status flags. If the application blindly assigns these values, the attacker gains unintended control over the object's state.

03Real-World Impact

Mass assignment vulnerabilities can lead to privilege escalation (a regular user setting themselves as an administrator), financial fraud (modifying transaction amounts or account balances), or bypassing business logic (changing order status, subscription tier, or approval flags). The severity depends on which attributes are exposed; in the worst case, an attacker can take over accounts or modify critical application state without authentication.

04Vulnerable & Fixed Patterns

Vulnerable pattern
from flask import Flask, request

app = Flask(__name__)

class User:
    def __init__(self):
        self.username = None
        self.email = None
        self.is_admin = False
        self.account_balance = 0.0

@app.route('/update_profile', methods=['POST'])
def update_profile():
    user = User()
    # Dangerous: directly assign all request parameters to object attributes
    for key, value in request.form.items():
        setattr(user, key, value)
    # user.is_admin or user.account_balance could now be modified by attacker
    save_user(user)
    return "Profile updated"

Why it's vulnerable:
The loop blindly assigns every form parameter to the user object without checking which attributes are safe to modify. An attacker can include is_admin=True or account_balance=999999 in the request and the application will set those fields.

Fixed pattern
from flask import Flask, request

app = Flask(__name__)

class User:
    def __init__(self):
        self.username = None
        self.email = None
        self.is_admin = False
        self.account_balance = 0.0

@app.route('/update_profile', methods=['POST'])
def update_profile():
    user = User()
    # Only allow specific, safe attributes to be modified
    allowed_fields = {'username', 'email'}
    for key in allowed_fields:
        if key in request.form:
            setattr(user, key, request.form[key])
    save_user(user)
    return "Profile updated"
Vulnerable pattern
<?php
class User {
    public $username;
    public $email;
    public $is_admin = false;
    public $account_balance = 0.0;
}

$user = new User();
// Dangerous: assign all POST parameters directly to object properties
foreach ($_POST as $key => $value) {
    $user->$key = $value;
}
// $user->is_admin or $user->account_balance could now be modified
save_user($user);
?>

Why it's vulnerable:
The loop assigns every POST parameter as a property on the user object without validation. An attacker can submit is_admin=1 or account_balance=999999 and those properties will be set.

Fixed pattern
<?php
class User {
    public $username;
    public $email;
    public $is_admin = false;
    public $account_balance = 0.0;
}

$user = new User();
// Only allow specific, safe attributes to be modified
$allowed_fields = array('username', 'email');
foreach ($allowed_fields as $field) {
    if (isset($_POST[$field])) {
        $user->$field = sanitize_input($_POST[$field]);
    }
}
save_user($user);
?>

05Prevention Checklist

Define an explicit allowlist
of attributes that users are permitted to modify; never use a blocklist or assume "obvious" fields are safe.
Use framework features
that support attribute whitelisting (e.g., Rails attr_accessible, Django model fields, or similar mechanisms in your framework).
Separate input models from domain models
— accept user input into a dedicated DTO or form object with only safe fields, then manually copy to your core domain object.
Validate and sanitize
all user input before assignment, even for allowed fields.
Review object definitions
regularly to identify sensitive attributes (roles, permissions, internal flags, financial data) that should never be user-modifiable.
Test with unexpected parameters
— include extra fields in requests during security testing to verify they are rejected or ignored.

06Signs You May Already Be Affected

Check your application logs and request handlers for evidence of unintended attribute modifications. Look for unexpected changes to sensitive fields (role changes, permission grants, balance adjustments) that correlate with user requests containing extra parameters. Review your codebase for loops or generic assignment patterns that iterate over all request parameters without an allowlist; these are high-risk patterns even if not yet exploited.

07Related Recent Vulnerabilities