Back to Technical Mode

Mass Assignment

Frameworks that let you assign a whole JSON object onto a model in one line are convenient — until a client includes a field like isAdmin or accountBalance that the form was never meant to expose, and the framework happily writes it because nothing told it not to.

The vulnerability is the absence of an allowlist — 'bind whatever fields arrive' silently trusts the client to only ever send the fields the developer intended
This most often surfaces on 'update profile' style endpoints, where the visible form has three fields but the underlying model has fifteen, several of which are security-sensitive
An allowlist (explicitly naming which fields may be set from this request) is strictly safer than a blocklist (naming which fields may not) — a blocklist has to be remembered and updated every time a new sensitive field is added to the model
This is conceptually the inverse of IDOR: IDOR is reading a record you shouldn't be able to see, mass assignment is writing a field you shouldn't be able to set

Want this flow explained in detail as it plays?

PATCH/api/users/me

Headers

Host:app.flowground.dev
Content-Type:application/json

Body

Loading...

Quick-Insert Payloads

Defense in depth

  • Explicitly allowlist which fields an endpoint accepts from the client and construct the update from only those named fields — never bind an entire request body onto a database model directly
  • Keep security-sensitive fields (roles, balances, verification flags) out of any model that's ever mass-assigned from client input, updating them only through dedicated, narrowly-scoped endpoints with their own authorization checks
  • Review every use of a framework's convenience mass-assignment helper (e.g. Model.update(req.body)) specifically for this pattern — it's easy to reach for the convenient version without thinking about which fields exist on the model

Vulnerable vs. Fixed

app.patch("/api/users/me", requireAuth, async (req, res) => {
  await db.users.update({ id: req.user.id }, req.body); // binds the ENTIRE request body onto the user model
  res.json({ success: true });
});