Mass Assignment Vulnerabilities: Exploiting Parameter Binding in Rails, Laravel, and Node.js
Mass assignment is a class of vulnerability that occurs when a web framework automatically binds HTTP request parameters to the properties of a model object — without restricting which properties can be set. The result: an attacker can set any model field by including it in a request, including fields that should never be user-controlled like role, admin, balance, or verified.
How Mass Assignment Works
Modern frameworks like Rails, Laravel, and Express provide convenient parameter binding. A controller action typically looks like:
# Ruby on Rails (vulnerable)
def update
@user.update(params[:user])
end
If the request body is {"user": {"name": "Alice", "role": "admin"}}, Rails will attempt to set both name and role on the model — including the dangerous role field.
Whitelisting vs. Blacklisting Approaches
The two defense strategies each have pitfalls:
- Blacklisting (explicitly excluding dangerous attributes) fails when developers add new sensitive fields and forget to update the blacklist. Rails used
attr_protectedfor this — it was removed from Rails 4+ because it was too easy to get wrong. - Whitelisting (explicitly allowing only safe attributes) is the correct approach. Rails uses Strong Parameters, Laravel uses
$fillable, and Node.js/Mongoose usesselect: falseor explicit schema fields.
Rails: Strong Parameters Bypass
Rails 4+ uses Strong Parameters to whitelist allowed fields:
# Secure Rails controller:
def user_params
params.require(:user).permit(:name, :email)
end
# Vulnerable if developer forgets to restrict:
def user_params
params.require(:user).permit! # Allows ALL params — dangerous!
end
When permit! is used, all nested parameters are allowed. Test by adding fields like admin: true, role: "superadmin", and confirmed_at: "2020-01-01" (to bypass email confirmation) to any PUT/PATCH request.
Laravel: fillable and guarded Bypass
Laravel's Eloquent models use $fillable (whitelist) or $guarded (blacklist):
// Vulnerable: empty guarded array allows everything
class User extends Model {
protected $guarded = [];
}
// Vulnerable: guarded only excludes 'id', not 'role'
protected $guarded = ['id'];
Test by adding is_admin, role, email_verified_at, and password to registration or profile update requests. Even with a correct $fillable, nested relationship updates (e.g., user.profile.admin_notes) are often unprotected.
Node.js / Mongoose: Schema Injection
In Express applications using Mongoose, a common vulnerable pattern:
// Vulnerable Express route
app.put('/api/users/:id', async (req, res) => {
const user = await User.findByIdAndUpdate(req.params.id, req.body, {new: true});
res.json(user);
});
The entire req.body is passed directly to the update operation. An attacker can set any field defined in the Mongoose schema, and potentially MongoDB operators:
# Elevate to admin:
{"name": "Alice", "role": "admin", "balance": 99999}
# MongoDB operator injection (if not sanitized):
{"$set": {"role": "admin"}}
Practical Attack: JSON Field Injection
A typical bug bounty attack path against a user profile update endpoint:
# Step 1: Capture a normal profile update request
PATCH /api/v1/profile
Content-Type: application/json
Authorization: Bearer user_token
{"display_name": "Alice Johnson"}
# Step 2: Add privilege escalation fields
{"display_name": "Alice Johnson", "role": "admin", "is_admin": true, "credit_balance": 9999, "email_verified": true}
# Step 3: Verify impact
GET /api/v1/profile
# Look for admin:true, role:admin, elevated balance in the response
Use the API Security Studio to intercept and replay these requests efficiently.
Real-World Impact Examples
- Account takeover via email change: Setting
emailon the user model without re-verification allows taking over any account by registering a new account and mass-assigning the victim's email. - Free subscription: Adding
plan: "enterprise",subscription_expires: "2099-12-31"to a profile update. - Financial fraud: Setting
account_balance,credit_limit, ordiscount_percentageto arbitrary values. - Verification bypass: Setting
phone_verified: true,kyc_status: "approved"to skip identity verification flows.
How to Test Systematically
- Identify all POST/PUT/PATCH endpoints that create or update user-owned resources.
- Note the fields returned in the response — they hint at the underlying model's schema.
- Add extra fields to every request: try common names like
admin,role,is_admin,verified,active,balance,credit,plan. - Check the response or a subsequent GET request to see if any fields were applied.
- For Rails apps, check if any routes use
permit!in the params method. - For Node.js apps, look for
req.bodypassed directly to a database update function.
Related Testing
Mass assignment overlaps significantly with BOLA/IDOR — after assigning admin role to your account, test BOLA on all API endpoints with admin privileges. See our OWASP API Security Top 10 guide for the complete API testing methodology. Use the Recon Hub to discover all API endpoints before starting systematic testing.
Testing for Mass Assignment with Burp Suite
When intercepting API requests with Burp Suite, add extra JSON fields to every PATCH, PUT, and POST body. A systematic approach is to first send a GET request to the same resource and note every field returned in the response — the full set of model properties. Then include those same fields in modification requests to see which ones the server actually updates. Pay special attention to boolean flags like is_admin, verified, premium, and numeric fields like role_id, access_level, plan_tier.
Another valuable technique is comparing the request body against the response body. If you send three fields and the response contains ten, those extra seven fields are part of the model and potentially writable. The Param Miner Burp extension can automatically guess hidden parameters by fuzzing common field name patterns derived from the endpoint and model names visible in the response.
Mass Assignment in File Upload Endpoints
Mass assignment is not limited to JSON APIs. Multipart form-data file upload endpoints often include hidden fields alongside the file itself. Try adding extra form fields to upload requests — many frameworks process all submitted form fields through the same parameter binding mechanism as JSON endpoints. Add fields like owner_id, public, category, and approved to file upload requests and check whether they influence the stored record. Document any fields that persist in the response and explore their security implications — an approved field that bypasses content moderation can be as impactful as a privilege escalation.
Level up your security testing
Install the CLI
npx payload-playgroundExplore All Tools
Encoding, hashing, JWT & more
Browse Cheat Sheets
Quick-reference payload guides