Day 17 — I Built a Vulnerable API to Demonstrate a Mass Assignment Attack
Source: Dev.to
Introduction
Some vulnerabilities don’t need sophisticated exploits.
Sometimes all it takes is a backend that trusts user input a little too much. Today I built a small Flask API to demonstrate a subtle but dangerous issue called Mass Assignment.
Modern frameworks make development fast by automatically mapping user input into database objects. For example, when updating a user profile, developers often accept JSON data and apply it directly to the user record:
user.update(request.json)Convenient, but also dangerous. If the application blindly accepts every field sent by the client, an attacker can modify hidden or sensitive fields that were never meant to be user‑controlled. This is where Mass Assignment vulnerabilities appear.
Demonstration Setup
The Flask API includes:
- User login
- Profile viewing
- Profile update endpoint
- Admin panel
The SQLite database stores user information, including an internal field is_admin that normal users should never be able to modify. In the vulnerable version, the update endpoint trusts the client completely.
Vulnerable Update Logic
fields = ["username", "email", "password", "is_admin"]
for field in fields:
if field in data:
cur.execute(
f"UPDATE users SET {field}=? WHERE id=?",
(data[field], session["user_id"])
)Because the server accepts whatever fields appear in the request body, a malicious payload like:
{
"email": "new@email.com",
"is_admin": 1
}will update the admin flag without any additional checks.
Exploit Walkthrough
Login as a normal user
POST /login Content-Type: application/json { "username": "testuser", "password": "password123" }The API returns a session cookie.
Send a crafted profile update
POST /update_profile Content-Type: application/json Cookie: { "email": "new@email.com", "is_admin": 1 }The server processes the request without validation and sets
is_adminto1.Access the admin panel
GET /admin Cookie:Response:
Welcome Admin. System secrets unlocked.
Privilege escalation is achieved in under 30 seconds from login to admin access.
Why Mass Assignment Happens
Mass Assignment vulnerabilities occur when:
- Frameworks automatically bind user input to objects.
- Sensitive fields exist in the model.
- Developers fail to restrict which fields can be updated.
Common contexts include:
- REST APIs
- ORMs (e.g., SQLAlchemy, ActiveRecord)
- Auto‑binding frameworks (e.g., Spring)
- JSON request handling
Real‑world impacts range from unauthorized role changes to over‑claiming discounts in e-commerce. OWASP lists this under A03:2021 – Injection (broader category), but it feels “just an update,” making it easy to overlook.
Mitigation: Field Whitelisting
Instead of accepting every parameter, explicitly define which fields users may modify.
allowed_fields = ["username", "email", "password"]
for field in allowed_fields:
if field in data:
cur.execute(
f"UPDATE users SET {field}=? WHERE id=?",
(data[field], session["user_id"])
)Now any attempt to modify is_admin is ignored, even if the attacker includes it in the request.
For ORMs, use model‑level protections such as:
@JsonIgnorein Jackson (Java)attr_readonlyin SQLAlchemy (Python)
These annotations enforce immutability of sensitive attributes.
Conclusion
This experiment shows that security flaws often hide in convenience. A single unvalidated parameter can turn a regular user into an administrator. When building APIs, audit your update endpoints for proper whitelisting. Tools like OWASP ZAP or a simple Burp Suite proxy can quickly spot these issues. Remember: user input should never dictate how internal system fields behave; the backend must always decide what can and cannot be modified.