VNX-PY-016 – Django Mass Assignment via Request Data Unpacking
Overview
This rule detects Django code that creates model instances by directly unpacking request data (e.g., Model.objects.create(**request.data)) or Django REST Framework serializers that expose all model fields via fields = '__all__'. Mass assignment occurs when an attacker includes unexpected fields in a request — such as is_staff, is_superuser, or price — and the application blindly persists them.
Severity: High | CWE: CWE-915 – Improperly Controlled Modification of Dynamically-Determined Object Attributes
Why This Matters
Mass assignment is one of the most exploited web application vulnerabilities because:
- It requires zero special tools — an attacker simply adds extra fields to a normal HTTP request
- It can escalate privileges instantly (
is_admin: true,role: "superuser") - It can manipulate business logic (
price: 0,discount: 100,verified: true) - It bypasses form-level validation because the attacker sends raw JSON/POST data
- Django and DRF will silently accept and persist extra fields unless explicitly restricted
The OWASP API Security Top 10 lists mass assignment as API6:2023 – Unrestricted Access to Sensitive Business Flows.
What Gets Flagged
Pattern 1: Direct request data unpacking into model creation
# Flagged: all request data flows into model fields
User.objects.create(**request.data)
Profile.objects.create(**request.POST)
Pattern 2: DRF serializer with fields=’__all__’
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
# Flagged: exposes every field including is_staff, is_superuser, password
fields = '__all__'
The rule applies only to .py files.
Remediation
Explicitly list the fields you intend to set. Never unpack all request data into a model:
# Safe: only the expected fields are used User.objects.create( username=request.data["username"], email=request.data["email"], )Use Django Forms or DRF Serializers with explicit field lists. These act as an allowlist for incoming data:
class UserSerializer(serializers.ModelSerializer): class Meta: model = User # Safe: only these three fields are accepted from input fields = ["username", "email", "bio"] # Extra safety: mark sensitive fields as read-only read_only_fields = ["is_staff", "is_superuser", "date_joined"]Use
read_only_fieldsfor sensitive attributes. Even with an explicit field list, mark fields that should never be set by users:class ProductSerializer(serializers.ModelSerializer): class Meta: model = Product fields = ["name", "description", "price", "category"] read_only_fields = ["price"] # Only admins can change priceValidate and clean incoming data before model operations. Use Django’s form validation:
form = UserCreationForm(request.POST) if form.is_valid(): user = form.save() # Only form-declared fields are savedUse
excludecautiously — prefer explicitfields. Whileexcludeworks, it’s fragile: adding a new model field automatically exposes it unless you remember to update the exclusion list.
References
- CWE-915: Improperly Controlled Modification of Dynamically-Determined Object Attributes
- OWASP API Security Top 10 – API6:2023 Mass Assignment
- OWASP Mass Assignment Cheat Sheet
- CAPEC-78: Using Slashes in Alternate Encoding
- MITRE ATT&CK T1565 – Data Manipulation
- Django REST Framework Serializers Documentation
- Django ModelForm Documentation