Firebase Realtime Database Security Checklist
Use this comprehensive checklist to audit your Firebase Realtime Database security rules and ensure your data is properly protected.
Pre-Deployment Checklist
Authentication & Access Control
- [ ] No world-readable paths - No
.read: truein production rules - [ ] Authentication required - All sensitive paths require
auth != null - [ ] User isolation - Users can only access their own data (
$uid === auth.uid) - [ ] Role-based access - Admin/moderator roles properly implemented
- [ ] Service account isolation - Backend services use separate authentication
Data Validation
- [ ] Type validation - All writes validate data types
- [ ] Required fields - Critical fields can't be null or missing
- [ ] String length limits - All string fields have
.lengthchecks - [ ] Number ranges - Numeric values within acceptable bounds
- [ ] Enum validation - Status fields only accept valid values
Write Protection
- [ ] Immutable fields - CreatedAt, userId can't be modified
- [ ] Ownership verification - Users can only modify their own content
- [ ] Delete protection - Critical data can't be deleted by users
- [ ] Cascading deletes - Rules handle related data cleanup properly
- [ ] Rate limiting - Consider Firebase Functions for rate-limited writes
Query & Index Security
- [ ] List operations restricted - Can't enumerate all users/documents
- [ ] Index rules defined -
.indexOnspecified for queries - [ ] Deep path protection - Nested objects properly secured
- [ ] Wildcard paths secured - All
$variablepaths have rules
Example Secure Rules
json
{
"rules": {
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid && !data.exists()",
"profile": {
".validate": "newData.hasChildren(['displayName', 'createdAt'])",
"displayName": {
".validate": "newData.isString() && newData.val().length > 0 && newData.val().length <= 50"
},
"createdAt": {
".validate": "newData.isNumber() && (!data.exists() || data.val() === newData.val())"
}
}
}
},
"posts": {
"$postId": {
".read": "auth != null",
".write": "auth != null && (!data.exists() || data.child('authorId').val() === auth.uid)",
".validate": "newData.hasChildren(['title', 'content', 'authorId', 'createdAt'])",
"authorId": {
".validate": "newData.val() === auth.uid && (!data.exists() || data.val() === newData.val())"
}
}
}
}
}Testing Your Rules
Automated Testing
bash
# Install FireScan
go install github.com/JacobDavidAlcock/firescan/cmd/firescan@latest
# Test unauthenticated access
firescan > set projectID your-project
firescan > set apiKey AIza...
firescan > scan --unauth --rtdb
# Test authenticated access
firescan > auth --create-account
firescan > scan --rtdb -l allManual Testing
Use Firebase Emulator for local testing:
bash
firebase emulators:start
# Test at http://localhost:9000Common Mistakes
- Forgetting
.validate- Rules without validation accept any data - Using only
.write- Need both.writeand.validate - Not testing cascading rules - Parent rules override child rules
- Ignoring
.indexOn- Queries fail without proper indexes - Weak timestamp validation - Accepting client-provided timestamps
Security Best Practices
- Start with deny-all, explicitly allow access
- Test rules before deploying to production
- Use Firebase Emulator for local development
- Implement server-side validation in Cloud Functions
- Monitor for suspicious activity in Firebase Console
- Regular security audits with automated tools
Next Steps
Need help? Get professional security assessment or support FireScan.
