What are Firebase Security Rules?
Firebase Security Rules provide a flexible, expression-based language to define who has read and write access to data stored in Firestore, Realtime Database, and Cloud Storage. Rules are evaluated on Firebase's servers, so they can't be bypassed by malicious clients.
Security Rules stand between your data and malicious users. They determine what data users can read and write, validate data structure, and enforce business logic—all without requiring a backend server.
🔒 Why Security Rules Matter
- 🛡️ Data Protection: Prevent unauthorized access to sensitive data.
- ✅ Data Validation: Ensure data meets your schema requirements.
- 👤 User Isolation: Keep users' data private from other users.
- 🚫 Rate Limiting: Prevent abuse with size and frequency limits.
Firestore Security Rules
Basic structure of Firestore rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Rules go here
// Match a specific collection
match /users/{userId} {
// Allow read if user is authenticated
allow read: if request.auth != null;
// Allow write only to own document
allow write: if request.auth != null && request.auth.uid == userId;
}
}
}
Common Firestore Rule Patterns
Practical examples for common scenarios:
User-owned Data
match /users/{userId} {
// Users can only read/write their own data
allow read, write: if request.auth != null && request.auth.uid == userId;
}
match /posts/{postId} {
// Anyone can read posts
allow read: if true;
// Only the author can create/update/delete
allow create: if request.auth != null
&& request.resource.data.authorId == request.auth.uid;
allow update, delete: if request.auth != null
&& resource.data.authorId == request.auth.uid;
}
Role-based Access
match /admin/{document=**} {
// Only allow access if user has admin custom claim
allow read, write: if request.auth != null
&& request.auth.token.role == 'admin';
}
match /moderator-content/{docId} {
// Allow access to admins and moderators
allow read, write: if request.auth != null
&& request.auth.token.role in ['admin', 'moderator'];
}
Data Validation
match /posts/{postId} {
allow create: if request.auth != null
// Required fields exist
&& request.resource.data.keys().hasAll(['title', 'content', 'authorId'])
// Title length validation
&& request.resource.data.title is string
&& request.resource.data.title.size() >= 1
&& request.resource.data.title.size() <= 100
// Content validation
&& request.resource.data.content is string
&& request.resource.data.content.size() <= 10000
// Author must be the current user
&& request.resource.data.authorId == request.auth.uid
// Timestamp must be server time
&& request.resource.data.createdAt == request.time;
allow update: if request.auth != null
&& resource.data.authorId == request.auth.uid
// Prevent changing certain fields
&& request.resource.data.authorId == resource.data.authorId
&& request.resource.data.createdAt == resource.data.createdAt;
}
Firestore Rule Functions
Create reusable functions for cleaner rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Helper function: Check if user is authenticated
function isAuthenticated() {
return request.auth != null;
}
// Helper function: Check if user owns the resource
function isOwner(userId) {
return isAuthenticated() && request.auth.uid == userId;
}
// Helper function: Check if user has a specific role
function hasRole(role) {
return isAuthenticated() && request.auth.token.role == role;
}
// Helper function: Check if user is admin
function isAdmin() {
return hasRole('admin');
}
// Helper function: Validate string length
function isValidString(field, minLen, maxLen) {
return field is string
&& field.size() >= minLen
&& field.size() <= maxLen;
}
// Helper function: Get user document
function getUserData() {
return get(/databases/$(database)/documents/users/$(request.auth.uid)).data;
}
// Use the functions
match /users/{userId} {
allow read: if isAuthenticated();
allow write: if isOwner(userId);
}
match /admin/{document=**} {
allow read, write: if isAdmin();
}
match /posts/{postId} {
allow read: if true;
allow create: if isAuthenticated()
&& isValidString(request.resource.data.title, 1, 100);
}
}
}
Subcollection Rules
Secure nested collections:
match /users/{userId} {
allow read, write: if request.auth.uid == userId;
// Subcollection: user's private notes
match /notes/{noteId} {
// Inherit parent's rules - only owner can access
allow read, write: if request.auth.uid == userId;
}
// Subcollection: user's public posts
match /publicPosts/{postId} {
// Anyone can read, only owner can write
allow read: if true;
allow write: if request.auth.uid == userId;
}
}
// Wildcard to match any subcollection depth
match /users/{userId}/{document=**} {
allow read, write: if request.auth.uid == userId;
}
Cloud Storage Security Rules
Secure file uploads and downloads:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
// User profile images
match /users/{userId}/avatar.{ext} {
// Anyone can read profile images
allow read: if true;
// Only owner can upload, with restrictions
allow write: if request.auth != null
&& request.auth.uid == userId
// Only allow image files
&& request.resource.contentType.matches('image/.*')
// Max 5MB file size
&& request.resource.size < 5 * 1024 * 1024;
}
// User's private files
match /users/{userId}/private/{allPaths=**} {
allow read, write: if request.auth != null
&& request.auth.uid == userId;
}
// Public uploads (e.g., blog images)
match /public/{allPaths=**} {
// Anyone can read
allow read: if true;
// Only authenticated users can upload
allow write: if request.auth != null
&& request.resource.contentType.matches('image/.*')
&& request.resource.size < 10 * 1024 * 1024;
}
// Restrict file types
match /documents/{userId}/{fileName} {
allow read: if request.auth.uid == userId;
allow write: if request.auth.uid == userId
// Allow only PDF and common document formats
&& request.resource.contentType in [
'application/pdf',
'application/msword',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
];
}
}
}
Realtime Database Security Rules
JSON-based rules for Realtime Database:
{
"rules": {
// Default: deny all access
".read": false,
".write": false,
"users": {
"$userId": {
// Users can read their own data
".read": "$userId === auth.uid",
// Users can write their own data with validation
".write": "$userId === auth.uid",
// Validate user data structure
".validate": "newData.hasChildren(['name', 'email'])",
"name": {
".validate": "newData.isString() && newData.val().length <= 50"
},
"email": {
".validate": "newData.isString() && newData.val().matches(/^[^@]+@[^@]+$/)"
}
}
},
"posts": {
// Anyone can read posts
".read": true,
"$postId": {
// Only authenticated users can create posts
".write": "auth != null && (!data.exists() || data.child('authorId').val() === auth.uid)",
".validate": "newData.hasChildren(['title', 'content', 'authorId'])",
"authorId": {
".validate": "newData.val() === auth.uid"
}
}
},
"presence": {
"$userId": {
// Users can only update their own presence
".write": "$userId === auth.uid"
}
}
}
}
Testing Security Rules
Test your rules before deploying:
# Use the Firebase Emulator
firebase emulators:start
# Run rules tests
npm test
# Test file: tests/firestore.rules.test.js
const { initializeTestEnvironment, assertFails, assertSucceeds } = require('@firebase/rules-unit-testing');
let testEnv;
beforeAll(async () => {
testEnv = await initializeTestEnvironment({
projectId: 'demo-project',
firestore: {
rules: fs.readFileSync('firestore.rules', 'utf8')
}
});
});
afterAll(async () => {
await testEnv.cleanup();
});
test('users can read their own data', async () => {
const userId = 'user123';
const context = testEnv.authenticatedContext(userId);
const db = context.firestore();
await assertSucceeds(db.collection('users').doc(userId).get());
});
test('users cannot read other users data', async () => {
const context = testEnv.authenticatedContext('user123');
const db = context.firestore();
await assertFails(db.collection('users').doc('other-user').get());
});
test('unauthenticated users cannot write', async () => {
const context = testEnv.unauthenticatedContext();
const db = context.firestore();
await assertFails(db.collection('users').doc('test').set({ name: 'Test' }));
});
Deploying Rules
Deploy your security rules:
# Deploy Firestore rules
firebase deploy --only firestore:rules
# Deploy Storage rules
firebase deploy --only storage
# Deploy Realtime Database rules
firebase deploy --only database
# Deploy all rules
firebase deploy --only firestore:rules,storage,database
# View deployed rules in Firebase Console
# Firestore: Firebase Console > Firestore > Rules
# Storage: Firebase Console > Storage > Rules
# Realtime DB: Firebase Console > Realtime Database > Rules
Common Security Mistakes
Avoid these common pitfalls:
Never do this:
// DANGEROUS: Allows anyone to read/write everything
match /{document=**} {
allow read, write: if true;
}
// DANGEROUS: Only checks authentication, not authorization
match /users/{userId} {
allow read, write: if request.auth != null;
}
Do this instead:
// SECURE: Check both authentication AND authorization
match /users/{userId} {
allow read, write: if request.auth != null
&& request.auth.uid == userId;
}
// SECURE: Validate all incoming data
match /posts/{postId} {
allow create: if request.auth != null
&& request.resource.data.authorId == request.auth.uid
&& request.resource.data.title is string
&& request.resource.data.title.size() > 0;
}
💡 Key Takeaways
- • Security Rules are your first line of defense against unauthorized access
- • Always validate both authentication AND authorization
- • Use functions to keep rules DRY and readable
- • Validate data structure and content in write rules
- • Test rules thoroughly before deploying to production
- • Never use allow read, write: if true in production
📚 Learn More
-
Security Rules Overview →
Complete guide to Firebase Security Rules across all services.
-
Firestore Security Rules Guide →
In-depth guide to securing your Firestore database.
-
Storage Security Rules Guide →
Secure your Cloud Storage buckets with custom rules.
-
Testing Security Rules →
Write unit tests for your security rules with the emulator.
-
Rules Language Reference →
Complete reference for the security rules language syntax.