Safeguard
Application Security•

ASVS 5.0 V15.3.3: Block Mass Assignment With Allowed Fields

ASVS V15.3.3 requires per action allowlists so clients cannot set fields like is_admin. Safe binding patterns for Rails, Express and Spring, with tests.

Safeguard Security Team
3 min read

ASVS 5.0 V15.3.3 requires protection against mass assignment by limiting which fields each controller and action accepts, so a field cannot be inserted or updated unless that action was meant to touch it. It is Level 2. Bind request data into a purpose built input type or an explicit allowlist, never straight into your database model.

Why it matters

Frameworks make it convenient to copy a JSON body onto an object. If the body contains "role": "admin" or "balance": 99999 and the model has those columns, the convenience becomes a privilege escalation. Public bug reports of this pattern go back more than a decade, and it keeps happening because the vulnerable code looks tidy.

How to implement it

Use a separate input type per action, containing only the fields that action allows.

Rails strong parameters:

def update
  @user = current_user
  @user.update!(params.require(:user).permit(:display_name, :time_zone))
  render json: @user
end

Express with an explicit pick:

const ALLOWED = ["displayName", "timeZone"];
app.patch("/me", requireAuth, async (req, res) => {
  const changes = Object.fromEntries(
    Object.entries(req.body).filter(([k]) => ALLOWED.includes(k))
  );
  await Users.update(req.user.id, changes);
  res.status(204).end();
});

Spring with a dedicated request object instead of the entity:

public record UpdateProfile(@Size(max = 80) String displayName, String timeZone) {}

@PatchMapping("/me")
ResponseEntity<Void> update(@Valid @RequestBody UpdateProfile in, Principal p) {
    service.updateProfile(p.getName(), in);
    return ResponseEntity.noContent().build();
}

Better still, reject unknown properties so the mistake is visible. In Jackson set FAIL_ON_UNKNOWN_PROPERTIES to true; in schema validators set additional properties to false.

Sensitive fields such as role, tenant id, owner id and price should be set by server logic from the session, never read from the body.

Click through it at your own pace, or open it full screen.

How to test it

  1. List the writable columns of each model, then compare to the fields each create and update endpoint is meant to accept.
  2. For every non intended field, send a request that includes it. Examples: role, is_admin, owner_id, tenant_id, price, verified, created_at.
  3. Fetch the record afterward. The value must not have changed.
  4. Test create and update separately, since their allowlists differ.
  5. Add the cases to your automated suite so new columns are covered.

Common mistakes

  • A denylist that omits the next sensitive column someone adds.
  • Using the same permissive input for admins and ordinary users.
  • Nested objects bound recursively, letting a child record be rewritten.
  • Allowlisting at the front end only.

Quick check

Does validating field types prevent mass assignment?

No. A well typed role string is still accepted. Only an allowlist of fields prevents the write.

Which level does it belong to?

Level 2.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.