Real-World Firebase Security Issues We've Found
After scanning hundreds of Firebase applications, we've discovered critical security vulnerabilities that exposed millions of user records. Here are real examples (anonymized) to learn from.
Case Study 1: Exposed Healthcare Records
The Vulnerability
A telehealth startup had world-readable patient data.
Their rules:
{
"rules": {
".read": true,
".write": "auth != null"
}
}The Impact
- 127,000 patient records accessible without authentication
- Personal health information (PHI) exposed
- Names, emails, medical conditions, prescriptions
- Potential HIPAA violation
- Found within 2 minutes of scanning
How We Found It
firescan > scan --unauth --rtdb
[RTDB] Critical Vulnerability Found!
├── Path: /patients
├── Records: 127,453
└── Data: emails, diagnoses, medicationsThe Fix
{
"rules": {
"patients": {
"$uid": {
".read": "$uid === auth.uid || root.child('doctors').child(auth.uid).child('patients').child($uid).val() === true",
".write": "root.child('doctors').child(auth.uid).val() === true"
}
}
}
}Lesson Learned
Never use .read: true in production. Always require authentication for sensitive data.
Case Study 2: E-commerce Order Manipulation
The Vulnerability
An e-commerce site let users modify any order.
Their rules:
match /orders/{orderId} {
allow read, write: if request.auth != null;
}The Impact
- Users could change order totals
- Users could view other customers' orders
- Shipping addresses exposed
- Payment information visible
- Estimated losses: $50,000+
Attack Vector
Any authenticated user could:
// View all orders
db.collection('orders').get().then(snap => {
console.log(`Found ${snap.docs.length} orders`);
});
// Modify order total
db.collection('orders').doc('any-order-id').update({
total: 0.01,
items: []
});The Fix
match /orders/{orderId} {
// Users can only read their own orders
allow read: if request.auth.uid == resource.data.userId;
// Users can only create orders for themselves
allow create: if request.auth.uid == request.resource.data.userId
&& request.resource.data.total is number
&& request.resource.data.total > 0;
// Orders can't be modified by users (only by Cloud Functions)
allow update, delete: if false;
}Lesson Learned
User isolation is critical. Users should only access their own data.
Case Study 3: Social Media Profile Takeover
The Vulnerability
A social networking app allowed users to modify critical fields.
Their rules:
match /profiles/{userId} {
allow read: if request.auth != null;
allow update: if request.auth.uid == userId;
}The Impact
- Users could set themselves as admin
- Users could modify their follower counts
- Users could change their email without verification
- Platform integrity compromised
Attack Vector
// Make yourself admin
db.collection('profiles').doc(myUserId).update({
role: 'admin',
isVerified: true,
followerCount: 1000000
});The Fix
match /profiles/{userId} {
allow read: if request.auth != null;
allow update: if request.auth.uid == userId
// Only allow updating safe fields
&& request.resource.data.diff(resource.data).affectedKeys()
.hasOnly(['displayName', 'bio', 'photoURL'])
// Validate field values
&& request.resource.data.displayName.size() <= 50
&& request.resource.data.bio.size() <= 500;
// Critical fields can only be modified by Cloud Functions
// role, isVerified, followerCount, etc.
}Lesson Learned
Whitelist editable fields. Protect critical fields from user modification.
Case Study 4: Cloud Function API Key Leak
The Vulnerability
A startup exposed their API keys in an unsecured Cloud Function.
Their code:
exports.sendEmail = functions.https.onRequest((req, res) => {
const apiKey = 'sk_live_ABC123...'; // Hardcoded!
sendgrid.setApiKey(apiKey);
// Send email
res.send('Sent');
});The Impact
- Anyone could call the function
- No authentication check
- API key exposed in function code
- Resulted in $3,000 in unauthorized email sends
- SendGrid account suspended
The Fix
exports.sendEmail = functions.https.onCall(async (data, context) => {
// Verify authentication
if (!context.auth) {
throw new functions.https.HttpsError('unauthenticated');
}
// Get API key from environment variable
const apiKey = functions.config().sendgrid.key;
// Rate limit
const userRef = admin.firestore().doc(`users/${context.auth.uid}`);
const user = await userRef.get();
if (user.data().emailsSentToday >= 10) {
throw new functions.https.HttpsError('resource-exhausted');
}
// Send email
sendgrid.setApiKey(apiKey);
// ... send email
// Update counter
await userRef.update({
emailsSentToday: admin.firestore.FieldValue.increment(1)
});
return { success: true };
});Lesson Learned
- Use
onCallinstead ofonRequest - Always check authentication
- Store secrets in environment variables
- Implement rate limiting
Case Study 5: File Upload Exploit
The Vulnerability
A file sharing service didn't validate uploads.
Their rules:
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}The Impact
- Users uploaded malicious files
- Users uploaded extremely large files (100GB+)
- Storage costs exceeded $10,000/month
- Malware distributed through the platform
Attack Vector
// Upload 100GB file
const hugeFile = new Blob([new ArrayBuffer(100 * 1024 * 1024 * 1024)]);
storage.ref('files/huge.bin').put(hugeFile);
// Upload malicious executable
storage.ref('files/malware.exe').put(malwareFile);The Fix
service firebase.storage {
match /b/{bucket}/o {
match /users/{userId}/{allPaths=**} {
// Restrict to user's own folder
allow read, write: if request.auth.uid == userId
// Limit file size to 10MB
&& request.resource.size < 10 * 1024 * 1024
// Only allow safe file types
&& request.resource.contentType.matches('image/.*|application/pdf');
}
}
}Plus Cloud Function for virus scanning:
exports.scanFile = functions.storage.object().onFinalize(async (object) => {
const filePath = object.name;
const contentType = object.contentType;
// Download file
const file = admin.storage().bucket().file(filePath);
const [buffer] = await file.download();
// Scan for malware
const isSafe = await virusScanner.scan(buffer);
if (!isSafe) {
console.warn(`Malware detected: ${filePath}`);
await file.delete();
// Notify user
const userId = filePath.split('/')[1];
await admin.firestore().doc(`users/${userId}`).update({
malwareDetected: true,
suspendedUntil: admin.firestore.Timestamp.now() + 86400000
});
}
});Lesson Learned
Always validate file uploads: size, type, and content.
Case Study 6: Query Enumeration Attack
The Vulnerability
A dating app allowed unrestricted user enumeration.
Their rules:
match /users/{userId} {
allow read: if request.auth != null;
}The Impact
- Competitors scraped entire user database
- 500,000 user profiles stolen
- Personal information sold on dark web
- Users' photos used without consent
Attack Vector
// Enumerate all users
const allUsers = [];
let lastDoc = null;
while (true) {
let query = db.collection('users').limit(1000);
if (lastDoc) {
query = query.startAfter(lastDoc);
}
const snapshot = await query.get();
if (snapshot.empty) break;
allUsers.push(...snapshot.docs.map(d => d.data()));
lastDoc = snapshot.docs[snapshot.docs.length - 1];
}
console.log(`Scraped ${allUsers.length} profiles`);The Fix
match /users/{userId} {
// Allow getting single user
allow get: if request.auth != null;
// But restrict list operations
allow list: if request.auth != null
// Only allow limited queries
&& request.query.limit <= 20
// Only specific indexes
&& request.query.orderBy in ['lastActive', 'createdAt']
// Must filter by location or interests (requires complex query)
&& request.query.where.size() >= 2;
}Lesson Learned
Separate get and list permissions. Restrict enumeration queries.
Statistics from Our Scans
From 1,000 scanned Firebase apps:
- 34% had world-readable databases
- 47% lacked proper user isolation
- 61% had no data validation
- 23% exposed PII (personally identifiable information)
- 8% had critical API vulnerabilities
- 92% had at least one security issue
How These Were Found
All vulnerabilities discovered using automated scanning:
# Install FireScan
go install github.com/JacobDavidAlcock/firescan/cmd/firescan@latest
# Test your app
firescan
firescan > set projectID your-project
firescan > set apiKey AIza...
firescan > scan --unauth
firescan > auth --create-account
firescan > scan --allAverage time to find critical vulnerabilities: 3 minutes
Prevention Checklist
Avoid these real-world mistakes:
- [ ] Never use
.read: trueorallow read, writein production - [ ] Always implement user isolation
- [ ] Whitelist editable fields
- [ ] Validate all data (type, length, format)
- [ ] Protect critical fields (role, verified, timestamps)
- [ ] Authenticate Cloud Functions
- [ ] Validate file uploads (size, type, scan for malware)
- [ ] Restrict query operations
- [ ] Store secrets in environment variables
- [ ] Test before deploying
- [ ] Use automated security scanning
Test Your App Now
Don't become the next case study. Test your Firebase app:
Or get professional help:
These are real vulnerabilities we found in production apps. They could have been prevented with proper security testing.
Protect your app: Get security assessment or support this project.
