Scribbling in the margin of the form
Watch it happen
Mallory adds extra fields in the margin of a three-box form. The teller copies all of them, so her role and balance change.
Mallory is a real customer coming in to update her details.
Mallory says: I need to update my details.In bank terms and app terms
| In the bank | In your app |
|---|---|
Are you exposed?
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.
Pick the fields you allow by name on the server, never spread the whole request body into the database. Validate bodies with a strict schema that rejects or strips unknown fields. Move sensitive fields (roles, balances, plans) to server-only code paths or separate tables users cannot write to.
It has happened to real companies
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.
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.
- Mass Assignment Cheat SheetOWASPAllow-list fields when binding; use DTOs with only user-editable fields; notes GitHub 2012
- API3:2023 Broken Object Property Level AuthorizationOWASPAvoid auto-binding client input to objects; allow changes only to client-editable properties
- Defining schemasZodZod objects strip unknown keys by default; strictObject rejects them
- Public Key Security Vulnerability and MitigationGitHubGitHub's March 2012 account of the mass assignment flaw in the key form
- How Homakov hacked GitHub and how to protect your applicationGitHub GistChanged user_id on his own SSH key to a Rails member to push to Rails
- Column Level SecuritySupabaseBlock users from updating sensitive columns with column privileges
- Hacking Rails and GitHubChris ShiflettEgor Homakov used mass assignment to add his key to the Rails organisation
Spot a mistake or something out of date? Report a correction · How these guides are made and checked
The free, open-source scanner checks for most of the problems in these guides.