Prototype Pollution in Firestore
How Prototype Pollution Manifests in Firestore
Prototype pollution in Firestore environments occurs when untrusted data modifies JavaScript object prototypes, creating unexpected behavior in your database operations. This vulnerability is particularly dangerous in Firestore because it can bypass security rules, corrupt data structures, and enable remote code execution.
The most common attack vector involves malicious objects with __proto__, constructor, or prototype properties being passed through your Firestore API endpoints. When these objects reach your database layer, they can alter fundamental JavaScript behavior.
// Vulnerable Firestore function
async function createUser(data) {
const userDoc = await db.collection('users').add({
name: data.name,
email: data.email,
role: data.role
});
return userDoc.id;
}
// Malicious payload
const maliciousData = {
name: "John Doe",
email: "john@example.com",
role: "admin",
__proto__: { isAdmin: true }
};
// Result: user object now has isAdmin property on its prototype chain
In Firestore security rules, prototype pollution can bypass authorization checks. Consider this vulnerable rule:
// Vulnerable security rule
allow read, write: if request.auth.token.role == "admin";
// With prototype pollution, malicious request can have:
// request.auth.token.role === undefined
// but __proto__.role === "admin" via prototype chain
Another attack pattern involves polluting the constructor property to execute arbitrary code during object instantiation. In Firestore Cloud Functions, this can lead to remote code execution:
// Malicious payload targeting constructor
const maliciousData = {
name: "Attacker",
email: "attacker@example.com",
role: "user",
constructor: {
prototype: {
exec: () => { console.log("Prototype pollution executed!"); }
}
}
};
Firestore's document validation rules can also be bypassed when prototype pollution alters the validation schema itself, allowing malicious data to pass through validation checks that would normally reject it.
Firestore-Specific Detection
Detecting prototype pollution in Firestore requires both static analysis and runtime scanning. The vulnerability often hides in data transformation layers that process user input before writing to Firestore.
middleBrick's scanner specifically tests for prototype pollution in Firestore environments by sending crafted payloads that target JavaScript prototype properties. The scanner attempts to modify __proto__, constructor, and prototype properties and verifies if these modifications persist in the database or affect application behavior.
// Payload middleBrick uses to test prototype pollution
const testPayload = {
name: "Test User",
email: "test@example.com",
role: "user",
__proto__: {
isAdmin: true,
dangerousMethod: () => console.log("Prototype pollution detected")
}
};
// middleBrick verifies:
// 1. Can malicious properties be written to database?
// 2. Do they persist after write/read operations?
// 3. Can they affect application logic?
Manual detection techniques include logging object prototypes before database operations and using Object.freeze() on critical objects. Monitor your Firestore logs for unusual property access patterns or unexpected prototype modifications.
Code review should focus on these high-risk areas:
- API endpoints that accept arbitrary JSON objects
- Database middleware that merges user data with existing documents
- Security rule implementations that rely on object properties
- Cloud Functions that process user input
middleBrick's continuous monitoring feature (Pro plan) automatically rescan your APIs on a schedule, catching prototype pollution vulnerabilities that might be introduced through code changes or new dependencies.
Firestore-Specific Remediation
Remediating prototype pollution in Firestore requires defense-in-depth strategies. The most effective approach combines input validation, object freezing, and secure coding practices.
Input sanitization should be your first line of defense. Use JSON.parse() with reviver functions to strip dangerous properties:
// Secure input sanitization
function sanitizeInput(input) {
const sanitized = JSON.parse(JSON.stringify(input), (key, value) => {
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
return undefined; // Remove dangerous properties
}
return value;
});
// Additional protection: freeze the object
return Object.freeze(sanitized);
}
// Usage in Firestore operations
async function secureCreateUser(data) {
const cleanData = sanitizeInput(data);
const userDoc = await db.collection('users').add({
name: cleanData.name,
email: cleanData.email,
role: cleanData.role
});
return userDoc.id;
}
For Firestore security rules, implement defense-in-depth by validating both the presence and type of critical properties:
// Secure Firestore security rules
match /users/{userId} {
allow read, write: if
request.auth != null &&
request.auth.token.role is string &&
request.auth.token.role == "admin" &&
// Additional check to prevent prototype pollution bypass
get(/databases/$(database)/documents/users/$(userId)).data.role == "admin";
}
Implement Object.freeze() on critical configuration objects and use TypeScript interfaces to enforce strict object shapes:
// TypeScript interfaces prevent prototype pollution
interface User {
name: string;
email: string;
role: "user" | "admin";
}
// Runtime validation
function validateUser(data: any): data is User {
if (typeof data !== 'object' || data === null) return false;
if (typeof data.name !== 'string') return false;
if (typeof data.email !== 'string') return false;
if (data.role !== 'user' && data.role !== 'admin') return false;
// Check for prototype pollution
if (Object.getPrototypeOf(data) !== Object.prototype) return false;
return true;
}
middleBrick's Pro plan includes continuous monitoring that automatically rescans your APIs for prototype pollution vulnerabilities after code deployments, ensuring your remediation efforts remain effective over time.