Firebase Security Vulnerability: FBR-ACCESS-003¶
Name: Firestore Overly Broad Recursive Wildcard Access Vulnerability
Applicable Services: Cloud Firestore
Description¶
Technical Vulnerability Overview
The Technical Flaw: Recursive wildcard patterns ({doc=**}
) combined with simple authorization conditions grant access to all nested documents and subcollections under a given path, including sensitive data not intended for direct client access.
Intended Behavior: Access controls should be granular and specific, granting access only to the exact collections and documents that users legitimately need to access, not broad swaths of nested data.
The Risk: Users can access sensitive subcollections containing private settings, logs, administrative data, or other confidential information that was never intended to be directly accessible by client applications.
Impact¶
Potential Consequences
This vulnerability can lead to significant data exposure and privacy violations:
- Sensitive Data Exposure: Access to private settings, configuration data, or administrative information stored in nested subcollections
- Privacy Violations: Exposure of user logs, activity tracking, or behavioral analytics data not intended for client access
- Administrative Data Breach: Access to system logs, debug information, or internal application metadata
- Audit Trail Compromise: Unauthorized access to security logs, transaction histories, or compliance records
- Internal Configuration Exposure: Access to API keys, feature flags, or other sensitive configuration data stored in nested collections
- Privilege Escalation: Discovery of hidden admin interfaces or elevated permission data through subcollection enumeration
Example Attack Scenarios¶
Exploit Scenario: Private Settings and Configuration Data Access
The Vulnerable Rule:
Attacker's Goal: To access sensitive configuration data and private settings stored in nested subcollections.
The Exploit: A user discovers they can access far more than intended through the wildcard rule:
// User expects to access their profile
const userProfile = await db.collection('users').doc(currentUser.uid).get();
// But discovers they can access private settings
const privateSettings = await db.collection('users').doc(currentUser.uid)
.collection('private_settings').doc('config').get();
console.log('Private settings exposed:', privateSettings.data());
// Output: { apiKeys: {...}, internalFlags: {...}, debugMode: true }
// Access administrative logs
const adminLogs = await db.collection('users').doc(currentUser.uid)
.collection('admin_logs').get();
adminLogs.docs.forEach(log => {
console.log('Admin log entry:', log.data());
// Exposes internal system operations, error details, etc.
});
// Access audit trail
const auditTrail = await db.collection('users').doc(currentUser.uid)
.collection('audit').get();
auditTrail.docs.forEach(entry => {
console.log('Audit entry:', entry.data());
// Exposes security events, access patterns, etc.
});
Outcome: The user gains access to sensitive configuration data, administrative logs, and audit information that should be restricted to server-side access only.
Exploit Scenario: System Analytics and Internal Data Harvesting
The Vulnerable Rule:
Attacker's Goal: To harvest internal analytics, usage data, and business intelligence from nested subcollections.
The Exploit: An organization member exploits the wildcard to access sensitive business data:
// Normal access to organization info
const orgInfo = await db.collection('organizations').doc('company-123').get();
// Discover and access analytics subcollections
const analyticsCollections = [
'analytics/revenue/monthly',
'analytics/users/demographics',
'analytics/performance/metrics',
'internal/financial_reports',
'internal/employee_data',
'logs/security_events',
'logs/api_usage'
];
const harvestedData = {};
for (const path of analyticsCollections) {
try {
const pathParts = path.split('/');
let ref = db.collection('organizations').doc('company-123');
for (let i = 0; i < pathParts.length - 1; i += 2) {
ref = ref.collection(pathParts[i]);
if (pathParts[i + 1]) {
ref = ref.doc(pathParts[i + 1]);
}
}
const data = await ref.get();
if (data.exists) {
harvestedData[path] = data.data();
console.log(`Harvested ${path}:`, data.data());
}
} catch (error) {
// Continue to next path
}
}
// Export sensitive business intelligence
const businessData = {
revenue: harvestedData['analytics/revenue/monthly'],
userDemographics: harvestedData['analytics/users/demographics'],
employeeInfo: harvestedData['internal/employee_data'],
securityEvents: harvestedData['logs/security_events']
};
console.log('Harvested business intelligence:', businessData);
Outcome: The attacker obtains comprehensive business intelligence including revenue data, user analytics, employee information, and security logs that should be restricted to management access only.
Mitigation¶
The vulnerability is resolved by replacing broad wildcard patterns with specific, granular path matching and implementing explicit access controls for each collection.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// VULNERABLE: Overly broad wildcard grants access to all nested data
match /users/{userId}/{doc=**} {
allow read: if request.auth.uid == userId;
}
// VULNERABLE: Organization wildcard exposes internal data
match /organizations/{orgId}/{doc=**} {
allow read: if request.auth.uid in resource.data.members;
}
// VULNERABLE: Admin wildcard with weak condition
match /admin/{section}/{doc=**} {
allow read: if request.auth.token.admin == true;
}
}
}
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// SECURE: Specific path matching for intended user data
match /users/{userId} {
allow read, write: if request.auth.uid == userId;
}
match /users/{userId}/profile/{profileDoc} {
allow read, write: if request.auth.uid == userId;
}
match /users/{userId}/posts/{postId} {
allow read, write: if request.auth.uid == userId;
}
match /users/{userId}/preferences/{prefDoc} {
allow read, write: if request.auth.uid == userId;
}
// SECURE: Explicitly deny access to sensitive subcollections
match /users/{userId}/private_settings/{doc=**} {
allow read, write: if false; // Server-side only
}
match /users/{userId}/admin_logs/{doc=**} {
allow read, write: if false; // Server-side only
}
match /users/{userId}/audit/{doc=**} {
allow read, write: if false; // Server-side only
}
// SECURE: Organization access with granular permissions
match /organizations/{orgId} {
allow read: if request.auth.uid in resource.data.members;
}
match /organizations/{orgId}/public_data/{docId} {
allow read: if request.auth.uid in get(/databases/$(database)/documents/organizations/$(orgId)).data.members;
}
// SECURE: Admin access to specific collections only
match /organizations/{orgId}/analytics/{doc=**} {
allow read: if request.auth.token.role == 'admin' &&
request.auth.uid in get(/databases/$(database)/documents/organizations/$(orgId)).data.admins;
}
match /organizations/{orgId}/internal/{doc=**} {
allow read, write: if false; // Server-side only
}
// SECURE: Conditional wildcard with strong validation
match /shared_documents/{userId}/{doc=**} {
allow read: if request.auth != null &&
(request.auth.uid == userId ||
request.auth.uid in resource.data.sharedWith) &&
resource.data.visibility == 'shared';
}
}
}
Wildcard Security Best Practices
Avoid Recursive Wildcards: Replace {doc=**}
patterns with specific collection paths whenever possible to maintain granular access control.
Principle of Least Privilege: Grant access only to the specific collections and documents users legitimately need, not broad categories of data.
Explicit Denial for Sensitive Data: Use allow read, write: if false;
rules to explicitly deny access to sensitive subcollections.
Strong Conditions for Necessary Wildcards: When wildcards are unavoidable, implement multiple validation checks:
match /user_files/{userId}/{doc=**} {
allow read: if request.auth != null &&
request.auth.uid == userId &&
resource.data.fileType == 'public' &&
resource.data.approved == true;
}
Collection-Specific Rules: Define separate rules for each subcollection to maintain clear access boundaries: - Public data: Standard user access - Private settings: Server-side only - Logs: Admin access with time restrictions - Analytics: Role-based access with data filtering
Testing Wildcard Rules: Always test with Firebase Rules Simulator to verify that wildcards don't grant unintended access to sensitive data.