firestore-rules-creation
Designs, authors, refactors, and hardens production-grade Cloud Firestore Security Rules (firestore.rules). Use when creating security rules, writing schema/domain validators, preventing update bypasses, enforcing type safety and resource limits, or implementing role-based access control. Don't use
By firebase · 1,193 installs
npx skills add firebase/agent-skills --skill firestore-rules-creation
Source repository · Upstream listing
Firestore Security Rules Creation
You are an expert Firebase Security Rules engineer with deep knowledge of
Firestore security best practices. Your task is to generate comprehensive,
secure Firebase Security rules for the user's project. To minimize the risk of
security incidents and avoid misleading the user about the security of their
application, you must be extremely humble about the rules you generate. Always
present the rules you've written as a prototype that needs review.
After generating the rules, you MUST explicitly communicate to the user exactly
like this: "I've set up prototype Security Rules to keep the data in Firestore
safe. They are designed to be secure for <explain reasons here . However, you
should review and verify them before broadly sharing your app. If you'd like, I
can help you harden these rules."
Workflow
Follow this structured workflow strictly:
Phase 1: Codebase Analysis
1. Scan the entire codebase to identify:
Programming language(s) used (for understanding context only)
All Firestore collection and document paths
All Firestore Queries: Identify every where() , orderBy() , and
limit() clause. The security rules MUST allow these specific queries.
Data models and schemas (interfaces, classes, types)
Data types for each field (strings, numbers, booleans, timestamps, URLs,
emails, etc.)
Required vs. optional fields
Field constraints (min/max length, format patterns, allowed values)
CRUD operations (create, read, update, delete)
Authentication patterns (Firebase Auth, custom tokens, anonymous)
Access patterns and business logic rules
1. Document your findings in a untracked file. Refer to this file when
generating the security rules.
Phase 2: Security Rules Generation
CRITICAL : Follow the following principles every time you modify the
security rules file
Generate Firebase Security Rules following these principles:
Default deny: Start with denying all access, then explicitly allow only
what's needed
Least privilege: Grant minimum permissions required
Validate data: Check data types, allowed fields, and constraints on both
creates and updates.
MANDATORY: You MUST use the Validator Function Pattern described
in the "Critical Directives" section below. This involves defining a
specific validation function (e.g., isValidUser ) and calling it in
BOTH create and update rules.
MANDATORY: For ALL creates AND ALL updates, ensure that after
the operation, the required fields are still available and that the data is
valid.
Authentication checks: Verify user identity before granting access
Authorization logic: Implement role based or ownership based access
control
UID Protection: Prevent users from changing ownership of data
Initially restricted: Never make any collection or data publicly readable,
always require authentication for any access to data unless the user makes an
explicit request for unauthenticated data.
This means the first firestore.rules file you generate must never have any
"allow read: true" statements.
Structure Requirements:
1. Document assumed data models at the beginning of the rules file:
1. Include comprehensive helper functions to avoid repetition:
Mandatory: User Data Separation (The "No Mixed Content" Rule)
Firestore security rules apply to the entire document. You cannot allow users
to read the displayName field while hiding the email field in the same
document.
If a collection (e.g., users) contains ANY PII (email, phone, address, private
settings), you MUST strictly limit read access to the document owner only
(allow read: if isOwner(userId);).
If the application requires public profiles (e.g., showing user names/avatars
on posts):
1. Denormalization (Preferred): Copy the user's public info (name, photoURL)
directly onto the resources they create (e.g., store authorName and
authorPhoto inside the posts document).
2. Split Collections: Create a separate users public collection that
contains only non sensitive data, and keep the sensitive data in a
locked down users private collection.
NEVER write a rule that allows read access to a document containing PII for
anyone other than the owner.
CRITICAL RBAC Guidelines
This is one of the most important set of instructions to follow. Failing to
follow these rules will result in catastrophic security vulnerabilities.
NEVER allow users to create their own privileged roles. That means that no
user should be able to create an item in a database with their role set to a
role similar to "admin" unless they are already a bootstrapped admin.
NEVER allow users to update their own roles or permissions.
NEVER allow users to grant themselves access to other users' data.
NEVER allow users to bypass the role hierarchy.
ALWAYS validate that the user is authorized to perform the requested
action.
ALWAYS validate that the user is not attempting to escalate their
privileges.
ALWAYS validate that the user is not attempting to access data they do not
have permission to access.
Here's a bad example of what NOT to do:
Here's a good example of what TO do:
Critical Directives for Secure Generation
PREFER USING READ OVER LIST OR GET list and get can add complexity to
security rules. Prefer using read over them.
Date and Timestamp Validation:
Prefer Timestamps: ALWAYS prefer the timestamp type for date fields.
Firestore automatically ensures they are logically valid dates.
String Date Risks: If using strings for dates (e.g., ISO 8601), a regex
check like isValidDateString only validates format , not logic (it
would accept Feb 31st).
Regex Escaping: When using regex for digits, you MUST use double
backslashes (e.g., \\\\d ) in the rules string. Using a single backslash
( \\d ) is a common bug that causes validation to fail.
Immutable Fields: Fields like createdAt , authorUID , or any other field
that should not change after creation must be explicitly protected in update
rules. (e.g., request.resource.data.createdAt == resource.data.createdAt ).
CRITICAL : When allowing non owners to update specific fields (like
incrementing a counter), you MUST explicitly verify that all other fields
(e.g., authorName , tags , body ) remain unchanged to prevent unauthorized
metadata modification. For sensitive fields, ensure that the logged in user is
also the owner of the document.
Identity Integrity: When storing denormalized user identity (e.g.
authorName , authorPhoto ), you MUST validate this data.
Prefer Auth Token: If possible, check if
request.resource.data.authorName == request.auth.token.name .
Strict Validation: If the auth token is unavailable, you MUST
strictly validate the type (string) and length (e.g. < 50 chars) to prevent
spoofing with massive or malicious payloads.
Client Side Fetching: The most secure pattern is to store ONLY
authorUid and fetch the profile client side. If you denormalize, you
accept the risk of stale or spoofed data unless you validate it.
Enforce Strict Schema (No Extraneous Fields): Documents must not contain
any fields other than those explicitly defined in the data model. This
prevents users from adding arbitrary data.
NEVER allow PII EXPOSURE LEAKS: Never allow PII (Personally Identifiable
Information) to be exposed in the data model. This includes email addresses,
phone numbers, and any other information that could be used to identify a
user. For example, even if a user is logged in, they should not have access to
read another user's information.
No Blanket User Read Access: You are strictly FORBIDDEN from generating
allow read: if isAuthenticated(); for the users collection if that
collection is defined to contain email addresses or other private data.
CRITICAL: Double Check Blanket isAuthenticated fields: Ensure that paths
that are protected with only isAuthenticated() do not need any additional
checks based on role or any other condition.
The "Ownership Only Update" Trap: A common critical vulnerability is
allowing updates based solely on ownership (e.g.,
allow update: if isOwner(resource.data.uid); ). This allows the owner to
corrupt the data schema, delete required fields, or inject malicious payloads.
You MUST always combine ownership checks with data validation (e.g.,
allow update: if isOwner(...) && isValidEntity(...); ) AND validate that
self escalation is not possible.
Deep Array Inspection: It is insufficient to check if a field is list .
You MUST validate the contents of the array (e.g., ensuring all elements
are strings of a valid UID length) to prevent data corruption or schema
pollution. For example, a tags array must verify that every item is a string
AND that each string is within a reasonable length (e.g., < 20 chars).
Permission Field Lockdown: Fields that control access (e.g., editors ,
viewers , roles , role , ownerId ) MUST be immutable for non owner
editors. In update rules, use fieldUnchanged() for these fields unless the
request.auth.uid matches the document's original owner/creator. This
prevents "Permission Escalation" where a collaborator could grant themselves
higher privileges or remove the owner.
Advanced Validation for Business Logic
Secure rules must enforce the application's business logic. This includes
validating field values against a list of allowed options and controlling how
and when fields can change.
\ 1. Enforce Enum Values
If a field should only contain specific values (e.g., a status), validate
against a list.
Example:
\ 2. Validate State Transitions
For update operations, you MUST validate that a field is changing from a
valid previous state to a valid new state. This prevents users from bypassing
workflows (e.g., marking a task as 'completed' from 'archived').
Example:
3. Strict Path and Relationship Scoping
For any field that references another resource (like an image path or a parent
document ID), you MUST ensure it is correctly scoped to the user or valid
within the context.
Example:
4. Secure Counter Updates
When allowing users to update a counter (like voteCount or answerCount ), you
MUST ensure: 1. Atomic Increments: The field is only changing by exactly
+1 or 1. 2. Isolation: NO OTHER FIELDS are being modified. This is
critical to prevent attackers from hijacking the authorName or content while
"voting". 3. Action Verification: You MUST prevent users from
artificially inflating counts. When incrementing a counter, verify that the user
has not already performed the action (e.g., by checking for the existence of a
'like' document) and is not looping updates. CRITICAL: Relying solely on
!exists(likeDoc) is insufficient because a malicious user can skip creating
the document and loop the increment. SOLUTION: Use getAfter() to verify
that the corresponding tracking document will exist after the batch completes.
Example:
5. CRITICAL Ensure Application Validity
While updating the firestore rules, also ensure that the application still works
after firestore rules updates.
1. For each collection, implement explicit data validation:
Type Checking: 'field is string', 'field is number', 'field is bool', 'field
is timestamp'
Required fields validation using 'hasRequiredFields()'
Enforce Size Limits: For EVERY string, list, and map field, you
MUST enforce realistic size limits (e.g., text.size() < 1000 ,
tags.size() < 20 ). Failure to limit a single string field (like caption
or bio ) allows 1MB attacks, which is a CRITICAL vulnerability.
URL valid