Stop 20 of 42 · Account Access · Mass assignment

Scribbling in the margin of the form

The update form has three boxes: name, address and phone. Mallory fills those in, then scribbles “role: admin” and “balance: 1,000,000” in the margin. The teller copies everything on the page onto her record without asking which parts were official.

Severity HighCategory Privilege EscalationAccount AccessSources checked 25 Oct 2026

Watch it happen

Play it as

Mallory adds extra fields in the margin of a three-box form. The teller copies all of them, so her role and balance change.

Arrival

Mallory is a real customer coming in to update her details.

Mallory says: I need to update my details.
1 / 5

In bank terms and app terms

In the bankIn your app
Boxes on the formThe fields your UI shows, like name and avatar_url
Margin scribblesExtra JSON fields in the request body, like role, is_admin, credits or plan
Teller copying everythingPassing the whole body to db.update or Model.create(req.body)
Allowed list on the deskPicking named fields, or validating with a schema (zod) that strips unknown keys

Are you exposed?

You might be exposed if…
  • Your update route does something like db.update(userId, body) or User.create(req.body).
  • Your user table has fields like role, is_admin, credits, plan or verified that the profile form should never touch.
  • You rely on the form not showing those fields, rather than the server refusing them.
What to do about it
  1. Pick the fields you allow by name on the server, never spread the whole request body into the database.
  2. Validate bodies with a strict schema that rejects or strips unknown fields.
  3. Move sensitive fields (roles, balances, plans) to server-only code paths or separate tables users cannot write to.

Track these in your to-do list →

It has happened to real companies

Real-world breaches

GitHub (2012) — Egor Homakov exploited a Rails mass assignment vulnerability to add his SSH key to the Rails core repository by manipulating the user_id attribute, gaining commit access to one of the most important open source projects.

What it looks like in code

// BAD: Accept all fields from request body
const body = await request.json()
await db.update("profiles", userId, body)  // Whatever the client sends gets written

// GOOD: Explicitly pick allowed fields
const body = await request.json()
const allowedUpdate = {
  display_name: body.display_name,
  avatar_url: body.avatar_url,
  // role and is_admin are NOT included
}
await db.update("profiles", userId, allowedUpdate)

Ask your AI tool to fix it

Paste this into Cursor, Claude, Lovable, Bolt or whatever you build with.

Prompt
Find every route that creates or updates a record using the request body (spread operators, req.body, request.json() passed straight to the database). Replace each with an explicit allow-list of fields, validated with a strict zod schema that strips unknown keys. Make sure role, is_admin, plan, credits and ownership fields can never be set from client input.

Where this comes from

The facts, figures and fixes on this page were checked against these sources on 25 Oct 2026. They are linked so you can check for yourself.

Spot a mistake or something out of date? Report a correction · How these guides are made and checked

Go deeper in isitsecure

The free, open-source scanner checks for most of the problems in these guides.