Of everything I've torn down on this blog, mass assignment tends to surprise people the most, mostly because the name gives no hint of what it actually does: let a completely ordinary update request quietly change fields that were never supposed to be editable at all.
What's actually happening
Many apps update a record by taking whatever fields were sent in a request and writing them straight to the matching database row, for convenience: it's less code than explicitly listing every field that's allowed to change. If a user record has a field like "role" or "isAdmin" sitting right alongside ordinary fields like "name" or "email," and the update logic writes whatever it's given without filtering, then whatever the request includes gets written, whether or not it was ever meant to be user-editable.
A concrete version of this
Imagine a normal "update your profile" form: name, bio, profile picture, nothing that looks sensitive. If the actual request sent behind the scenes is easy to inspect and modify, as it usually is through a browser's developer tools, and the server-side update logic accepts whatever fields it's handed, adding an extra field like "role": "admin" to that same request can, if nothing filters it out, get written straight into the account, alongside the harmless profile changes the form was actually built for.
Why this is so easy to build by accident
Writing an update function that accepts a specific, explicit list of allowed fields takes a bit more code than one that just accepts everything it's handed. The version that accepts everything works identically in every normal test, since a normal test only ever sends the fields the form is meant to send. The gap only appears the moment someone sends a request the form was never built to send.
How to check for it
- Find an update or profile-edit feature in your app, and inspect the actual request it sends using your browser's developer tools.
- Try adding an extra field to that request, one your app definitely shouldn't let a regular user set, like a role or permission field, if your data model has one.
- Send the modified request and check whether the extra field actually took effect.
- If it did, that's mass assignment, confirmed directly.
The fix
Update logic should explicitly list which fields a given request is allowed to change, rather than accepting whatever's handed to it, so that anything outside that explicit list is simply ignored regardless of what a request includes. It's a small, deliberate constraint to add, and one of the few vulnerabilities on this blog where the fix is less about adding a check and more about refusing to trust convenience over precision.