Firebase Security Rules Best Practices 2026
Firebase security best practices evolve as new attack vectors emerge and the platform matures. This guide covers current best practices for securing Firebase applications in 2026.
Core Principles
1. Default Deny, Explicit Allow
Always start with deny-all rules:
javascript
// Firestore
service cloud.firestore {
match /databases/{database}/documents {
// Deny everything by default
match /{document=**} {
allow read, write: if false;
}
// Then explicitly allow specific access
match /users/{userId} {
allow read, write: if request.auth.uid == userId;
}
}
}2. Principle of Least Privilege
Grant minimum necessary permissions:
javascript
// Bad: Too broad
allow read, write: if request.auth != null;
// Good: Specific permissions
allow get: if request.auth.uid == userId;
allow list: if false; // No listing
allow create: if request.auth.uid == userId;
allow update: if request.auth.uid == userId
&& !request.resource.data.diff(resource.data).affectedKeys().hasAny(['role']);
allow delete: if false; // No deletion3. Defense in Depth
Never rely on security rules alone:
javascript
// Client-side validation
if (title.length > 200) {
throw new Error('Title too long');
}
// Security rules validation
allow write: if request.resource.data.title.size() <= 200;
// Cloud Function validation
if (!isValidTitle(data.title)) {
throw new functions.https.HttpsError('invalid-argument');
}Authentication Best Practices
Use Firebase Authentication
javascript
// Always verify authentication
allow read, write: if request.auth != null;
// Never trust client-provided UIDs
// Bad:
allow write: if request.resource.data.userId == userId;
// Good:
allow write: if request.auth.uid == userId;Implement Custom Claims for Roles
javascript
// Set in Cloud Function
admin.auth().setCustomUserClaims(uid, { admin: true });
// Use in rules
allow write: if request.auth.token.admin == true;Verify Email Before Access
javascript
allow read: if request.auth != null
&& request.auth.token.email_verified == true;Data Validation Best Practices
Validate All Fields
javascript
allow create: if request.resource.data.keys().hasAll(['title', 'content', 'authorId', 'createdAt'])
&& request.resource.data.keys().hasOnly(['title', 'content', 'authorId', 'createdAt', 'tags'])
&& request.resource.data.title is string
&& request.resource.data.title.size() > 0
&& request.resource.data.title.size() <= 200
&& request.resource.data.content is string
&& request.resource.data.content.size() <= 10000
&& request.resource.data.authorId == request.auth.uid
&& request.resource.data.createdAt == request.time
&& (request.resource.data.tags == null || request.resource.data.tags is list);Use Functions for Complex Validation
javascript
function isValidPost() {
let post = request.resource.data;
return post.keys().hasAll(['title', 'content'])
&& post.title is string
&& post.title.size() > 0
&& post.title.size() <= 200
&& post.content is string;
}
allow create: if isValidPost();Protect Timestamps
javascript
// Force server timestamp
allow create: if request.resource.data.createdAt == request.time;
// Prevent timestamp modification
allow update: if request.resource.data.createdAt == resource.data.createdAt;Query Security Best Practices
Restrict List Operations
javascript
// Don't allow unrestricted listing
// Bad:
allow list: if request.auth != null;
// Good: Restrict by query parameters
allow list: if request.auth != null
&& request.query.limit <= 20
&& request.query.orderBy == 'createdAt'
&& request.auth.uid in request.query.where[0].value; // Only own dataUse Separate get/list Permissions
javascript
// Allow getting single documents
allow get: if request.auth.uid == userId;
// But deny listing all users
allow list: if false;Storage Best Practices
User-Specific Paths
javascript
service firebase.storage {
match /b/{bucket}/o {
match /users/{userId}/{allPaths=**} {
allow read, write: if request.auth.uid == userId;
}
}
}Validate File Size and Type
javascript
allow write: if request.resource.size < 5 * 1024 * 1024 // 5MB
&& request.resource.contentType.matches('image/.*');Scan for Malware
Use Cloud Functions:
javascript
exports.scanUpload = functions.storage.object().onFinalize(async (object) => {
// Integrate with virus scanning service
const isSafe = await scanFile(object.name);
if (!isSafe) {
await admin.storage().bucket().file(object.name).delete();
}
});Cloud Functions Best Practices
Always Verify Authentication
javascript
exports.sensitiveOperation = functions.https.onCall(async (data, context) => {
// Verify authentication
if (!context.auth) {
throw new functions.https.HttpsError('unauthenticated');
}
// Verify authorization
const userDoc = await admin.firestore().doc(`users/${context.auth.uid}`).get();
if (!userDoc.data().admin) {
throw new functions.https.HttpsError('permission-denied');
}
// Perform operation
});Use CORS Properly
javascript
const cors = require('cors')({ origin: true });
exports.api = functions.https.onRequest((req, res) => {
cors(req, res, () => {
// Only allow specific origins in production
if (process.env.NODE_ENV === 'production') {
if (req.headers.origin !== 'https://your-domain.com') {
res.status(403).send('Forbidden');
return;
}
}
// Handle request
});
});Input Validation
javascript
exports.createPost = functions.https.onCall(async (data, context) => {
// Validate all inputs
if (!data.title || typeof data.title !== 'string') {
throw new functions.https.HttpsError('invalid-argument', 'Invalid title');
}
if (data.title.length > 200) {
throw new functions.https.HttpsError('invalid-argument', 'Title too long');
}
// ... rest of function
});Testing Best Practices
Automate Security Testing
yaml
# In CI/CD
- name: Security Scan
run: firescan --config .firescan.yaml --json > results.json
- name: Fail on Critical Issues
run: |
if grep -q '"severity":"Critical"' results.json; then
exit 1
fiTest All Authentication States
bash
# Unauthenticated
firescan > scan --unauth
# Authenticated normal user
firescan > auth --create-account
firescan > scan --all
# Admin user (if applicable)
firescan > auth --admin
firescan > scan --allRegular Security Audits
Schedule monthly scans:
yaml
on:
schedule:
- cron: '0 2 1 * *' # First of each monthMonitoring Best Practices
Enable Firebase Security Rules Monitoring
javascript
// Log rule evaluations in development
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if debug(request.auth != null);
}
}
}Set Up Alerts
Monitor for:
- Unusual access patterns
- Failed authentication attempts
- Excessive permission denials
- Unexpected data modifications
Log Security Events
javascript
exports.onWrite = functions.firestore.document('users/{userId}').onWrite((change, context) => {
// Log all user document modifications
admin.firestore().collection('audit_log').add({
collection: 'users',
userId: context.params.userId,
timestamp: admin.firestore.FieldValue.serverTimestamp(),
before: change.before.data(),
after: change.after.data()
});
});Common Anti-Patterns to Avoid
❌ Don't Trust Client Data
javascript
// Bad: Trusting client-provided role
allow write: if request.resource.data.role == 'user';
// Good: Use auth token
allow write: if request.auth.token.role == 'user';❌ Don't Use Weak Checks
javascript
// Bad: Can be bypassed
allow write: if request.resource.data.userId != null;
// Good: Verify ownership
allow write: if request.resource.data.userId == request.auth.uid;❌ Don't Ignore Edge Cases
javascript
// Bad: Doesn't handle updates
allow write: if request.auth != null;
// Good: Different rules for create/update
allow create: if request.auth != null;
allow update: if request.auth.uid == resource.data.authorId;2026 Security Checklist
- [ ] All rules default to deny
- [ ] Authentication required for sensitive data
- [ ] User isolation properly implemented
- [ ] All writes validated (type, length, required fields)
- [ ] Timestamps server-controlled
- [ ] Critical fields protected from modification
- [ ] Query parameters restricted
- [ ] File uploads validated (size, type)
- [ ] Cloud Functions authenticate and authorize
- [ ] Automated security testing in CI/CD
- [ ] Monthly security audits scheduled
- [ ] Security monitoring and alerts enabled
- [ ] Audit logs for sensitive operations
- [ ] Regular updates to rules as app evolves
Next Steps
Stay ahead of security threats by following these best practices and testing regularly.
Need a professional security review? Get expert assessment or support this project.
