Mass Assignment in Firestore
How Mass Assignment Manifests in Firestore
Mass assignment vulnerabilities in Firestore occur when applications automatically map client-provided data to database objects without proper field filtering. This allows attackers to modify fields they shouldn't have access to by including them in API requests.
Consider a typical Firestore user profile update endpoint:
app.post('/api/user/update', async (req, res) => {
const userId = req.user.uid;
const updates = req.body; // Entire request body assigned directly
await db.collection('users').doc(userId).update(updates);
res.json({ success: true });
});An attacker can exploit this by sending additional fields:
POST /api/user/update
{
"displayName": "New Name",
"role": "admin", // Unauthorized field
"accountStatus": "active", // Unauthorized field
"credits": 999999 // Unauthorized field
}Firestore's flexible schema allows these fields to be written without schema validation, making the vulnerability particularly dangerous. The application trusts that the client only sends intended fields, but Firestore accepts any field the user provides.
Another common pattern involves using JavaScript's spread operator:
app.put('/api/users/:id', async (req, res) => {
const { id } = req.params;
const userData = { ...req.body }; // Spreads all request properties
await db.collection('users').doc(id).set(userData, { merge: true });
res.json({ success: true });
});This creates an even broader attack surface since req.body may contain unexpected properties from nested objects, arrays, or prototype pollution.
Firestore-specific mass assignment often appears in:
- User profile updates where clients can modify any field
- Admin panel endpoints that trust authenticated users
- Batch update operations that process multiple documents
- Import/export functionality that writes user-provided data
- Realtime database synchronization where client state is written back
The consequences include privilege escalation (changing user roles), data theft (exfiltrating sensitive fields), account takeover (modifying authentication tokens), and bypassing business logic (changing subscription status or quotas).
Firestore-Specific Detection
Detecting mass assignment vulnerabilities in Firestore requires both static code analysis and dynamic runtime scanning. middleBrick's Firestore-specific checks examine your API endpoints for patterns that could allow unauthorized field modifications.
middleBrick scans for these specific Firestore mass assignment indicators:
- Direct assignment of
req.bodyto Firestore update operations - Use of
set()withmerge: truewithout field whitelisting - Missing field validation before database operations
- Dynamic field names derived from user input
- Batch operations that process untrusted data
The scanner tests your endpoints by sending requests with unexpected fields and verifying whether they're accepted by Firestore:
POST /api/user/update
{
"allowedField": "safe",
"admin": true,
"billingInfo": "sensitive data",
"__proto__": { "polluted": true }
}middleBrick then checks if these unauthorized fields appear in the database, providing concrete evidence of the vulnerability.
Manual detection techniques include:
const allowedFields = ['displayName', 'email', 'phone'];
const updates = {};
for (const [key, value] of Object.entries(req.body)) {
if (allowedFields.includes(key)) {
updates[key] = value;
}
}
await db.collection('users').doc(userId).update(updates);Code review should specifically look for:
- Functions that accept entire request bodies without validation
- Firestore operations that use spread operators on user input
- Missing field whitelisting or blacklisting
- Dynamic field name construction using user data
- Batch operations that iterate over untrusted arrays
middleBrick's continuous monitoring in the Pro plan can detect when new endpoints are added that might introduce mass assignment vulnerabilities, alerting you before they reach production.
Firestore-Specific Remediation
Remediating mass assignment vulnerabilities in Firestore requires implementing strict field control and validation. The most effective approach is explicit whitelisting of allowed fields.
Basic whitelisting implementation:
const allowedFields = ['displayName', 'email', 'phone', 'preferences'];
const updates = {};
for (const field of allowedFields) {
if (req.body[field] !== undefined) {
updates[field] = req.body[field];
}
}
await db.collection('users').doc(userId).update(updates);This ensures only explicitly permitted fields can be modified, regardless of what the client sends.
For more complex scenarios, use field validation with schemas:
const Joi = require('joi');
const updateSchema = Joi.object({
displayName: Joi.string().max(100).optional(),
email: Joi.string().email().optional(),
phone: Joi.string().pattern(/^[\d\s\-\+\(\)]+$/).optional(),
// Explicitly exclude sensitive fields
});
app.put('/api/users/:id', async (req, res) => {
const { error, value } = updateSchema.validate(req.body, {
stripUnknown: true // Removes any fields not in schema
});
if (error) {
return res.status(400).json({ error: error.details[0].message });
}
await db.collection('users').doc(req.params.id).update(value);
res.json({ success: true });
});The stripUnknown: true option is critical as it automatically removes any fields not defined in the schema.
For batch operations, implement per-document validation:
app.post('/api/users/batch-update', async (req, res) => {
const updates = req.body.updates; // Array of { id, data }
const allowedFields = ['displayName', 'email'];
const batch = db.batch();
for (const update of updates) {
const filtered = {};
for (const field of allowedFields) {
if (update.data[field] !== undefined) {
filtered[field] = update.data[field];
}
}
batch.update(db.collection('users').doc(update.id), filtered);
}
await batch.commit();
res.json({ success: true });
});Firestore security rules provide an additional layer of defense:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read: if true;
allow update: if request.auth.uid == userId &&
request.resource.data.keys().hasOnly([
'displayName',
'email',
'phone'
]);
}
}
}The hasOnly() method ensures users can only modify the specified fields, providing a security boundary even if your application logic is bypassed.
For admin endpoints, implement role-based field access:
app.put('/api/admin/users/:id', async (req, res) => {
if (!req.user || !req.user.isAdmin) {
return res.status(403).json({ error: 'Forbidden' });
}
const adminFields = ['displayName', 'email', 'role', 'status'];
const filtered = {};
for (const field of adminFields) {
if (req.body[field] !== undefined) {
filtered[field] = req.body[field];
}
}
await db.collection('users').doc(req.params.id).update(filtered);
res.json({ success: true });
});middleBrick's Pro plan can scan your Firestore security rules alongside your API endpoints, ensuring your database-level protections complement your application-level fixes.
Related CWEs: propertyAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-915 | Mass Assignment | HIGH |
Frequently Asked Questions
Can Firestore security rules alone prevent mass assignment vulnerabilities?
How does middleBrick detect mass assignment in Firestore endpoints?
req.body assignment, use of merge: true without validation, and dynamic field construction. It provides specific findings with severity levels and remediation guidance.