הדף הזה מתבסס על המושגים שמופיעים במאמרים הבניית כללי אבטחה וכתיבת תנאים לכללי אבטחה, כדי להסביר איך Cloud Firestore Security Rules פועלים עם שאילתות. הוא בוחן מקרוב איך כללי האבטחה משפיעים על השאילתות שאפשר לכתוב, ומתאר איך לוודא שהשאילתות משתמשות באותן מגבלות כמו כללי האבטחה. בדף הזה מוסבר גם איך לכתוב כללי אבטחה כדי לאשר או לדחות שאילתות על סמך מאפייני שאילתה כמו limit ו-orderBy.
הנחיית AI לעזרה מבוססת-AI
כדי לקבל עזרה מ-AI, מעתיקים את ההנחיה שלמטה ומדביקים אותה בסוכן ה-AI לתכנות (כמו Gemini ב-Android Studio, Antigravity, Claude Code, Codex ו-Cursor). אתם יכולים להתאים אישית את ההנחיה ולשפר את הסוכן. מידע מפורט על ההתקנה זמין במאמר סקילים אג'נטיים ב-Firebase.
Help me use Firebase in my app.
1. Make sure that **Firebase agent skills** are installed and updated. For Claude Code, Codex, or Cursor agents, help me install the Firebase plugin. For other agents, run `npx skills add firebase/agent-skills`. Always try to use **Firebase agent skills** for Firebase-related tasks.
2. Help me write, validate, and deploy Firebase Security Rules to protect my app's Firestore data.
כללים הם לא מסננים
כשכותבים שאילתות לאחזור מסמכים, חשוב לזכור שכללי האבטחה הם לא מסננים – השאילתות הן הכל או כלום. כדי לחסוך לכם זמן ומשאבים, הכללים מעריכים שאילתה לפי קבוצת התוצאות הפוטנציאליות שלה במקום לפי ערכי השדות בפועל של כל המסמכים שלכם. אם שאילתה עשויה להחזיר מסמכים שללקוח אין הרשאה לקרוא, הבקשה כולה נכשלת.
שאילתות וכללי אבטחה
כפי שמוצג בדוגמאות הבאות, צריך לכתוב את השאילתות כך שיתאימו למגבלות של כללי האבטחה.
אבטחת מסמכים ושליחת שאילתות עליהם על סמך auth.uid
בדוגמה הבאה מוסבר איך לכתוב שאילתה כדי לאחזר מסמכים שמוגנים על ידי כלל אבטחה. נניח שיש מסד נתונים שמכיל אוסף של מסמכי story:
/stories/{storyid}
{
title: "A Great Story",
content: "Once upon a time...",
author: "some_auth_id",
published: false
}
בנוסף לשדות title ו-content, בכל מסמך מאוחסנים השדות author ו-published שמשמשים לבקרת גישה. בדוגמאות האלה אנחנו מניחים שהאפליקציה משתמשת באימות ב-Firebase כדי להגדיר את השדה author למזהה המשתמש שיצר את המסמך. בנוסף, Firebase Authentication מאכלס את המשתנה request.auth בכללי האבטחה.
כלל האבטחה הבא משתמש במשתנים request.auth ו-resource.data כדי להגביל את הרשאות הקריאה והכתיבה של כל story למחבר שלו:
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{storyid} {
// Only the authenticated user who authored the document can read or write
allow read, write: if request.auth != null && request.auth.uid == resource.data.author;
}
}
}
נניח שהאפליקציה כוללת דף שבו מוצגת למשתמש רשימה של story מסמכים שהוא יצר. אפשר להשתמש בשאילתה הבאה כדי לאכלס את הדף הזה. עם זאת, השאילתה הזו תיכשל כי היא לא כוללת את אותן מגבלות כמו כללי האבטחה:
לא תקין: האילוצים של השאילתה לא תואמים לאילוצים של כללי האבטחה
// This query will fail
db.collection("stories").get()
השאילתה נכשלת גם אם המשתמש הנוכחי הוא באמת המחבר של כל מסמך ב-story. הסיבה להתנהגות הזו היא שכאשר Rules מחיל את כללי האבטחה, הוא מעריך את השאילתה ביחס לקבוצת התוצאות הפוטנציאלית שלה, ולא ביחס למאפיינים בפועל של המסמכים במסד הנתונים. אם שאילתה עלולה לכלול מסמכים שמפירים את כללי האבטחה, השאילתה תיכשל.
לעומת זאת, השאילתה הבאה תצליח כי היא כוללת את אותה מגבלה על השדה author כמו כללי האבטחה:
תקין: אילוצי השאילתה תואמים לאילוצים של כללי האבטחה
var user = firebase.auth().currentUser;
db.collection("stories").where("author", "==", user.uid).get()
אבטחה של מסמכים ושליחת שאילתות לגביהם על סמך שדה
כדי להדגים עוד יותר את האינטראקציה בין שאילתות לכללים, כללי האבטחה הבאים מרחיבים את הרשאת הקריאה לאוסף stories כדי לאפשר לכל משתמש לקרוא מסמכי story שבהם השדה published מוגדר כ-true.
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{storyid} {
// Anyone can read a published story; only story authors can read unpublished stories
allow read: if resource.data.published == true || (request.auth != null && request.auth.uid == resource.data.author);
// Only story authors can write
allow write: if request.auth != null && request.auth.uid == resource.data.author;
}
}
}
השאילתה לדפים שפורסמו חייבת לכלול את אותן מגבלות כמו כללי האבטחה:
db.collection("stories").where("published", "==", true).get()
אילוץ השאילתה .where("published", "==", true) מבטיח שהערך של resource.data.published הוא true בכל תוצאה. לכן, השאילתה הזו עומדת בכללי האבטחה ומותר לקרוא נתונים.
OR שאילתות
כשמריצים שאילתה לוגית של OR (or, in או array-contains-any) על קבוצת כללים, המערכת בודקת כל ערך להשוואה בנפרד. כל ערך השוואה חייב לעמוד במגבלות של כללי האבטחה. לדוגמה, בכלל הבא:
match /mydocuments/{doc} {
allow read: if resource.data.x > 5;
}
לא חוקי: השאילתה לא מבטיחה ש-x > 5 לכל המסמכים הפוטנציאליים
// These queries will fail
query(db.collection("mydocuments"),
or(where("x", "==", 1),
where("x", "==", 6)
)
)
query(db.collection("mydocuments"),
where("x", "in", [1, 3, 6, 42, 99])
)
תקין: השאילתה מבטיחה ש-x > 5 לכל המסמכים הפוטנציאליים
query(db.collection("mydocuments"),
or(where("x", "==", 6),
where("x", "==", 42)
)
)
query(db.collection("mydocuments"),
where("x", "in", [6, 42, 99, 105, 200])
)
הערכת אילוצים בשאילתות
כללי האבטחה יכולים גם לאשר או לדחות שאילתות על סמך המגבלות שלהן.
המשתנה request.query מכיל את המאפיינים limit, offset ו-orderBy של שאילתה. לדוגמה, כללי האבטחה יכולים לדחות כל שאילתה שלא מגבילה את המספר המקסימלי של המסמכים שאוחזרו לטווח מסוים:
allow list: if request.query.limit <= 10;
קבוצת הכללים הבאה מדגימה איך לכתוב כללי אבטחה שמעריכים אילוצים שמוצבים על שאילתות. בדוגמה הזו, קבוצת הכללים הקודמת של stories הורחבה עם השינויים הבאים:
- קבוצת הכללים מפרידה את כלל הקריאה לכללים של
getו-list. - הכלל
getמגביל את השליפה של מסמכים בודדים למסמכים ציבוריים או למסמכים שהמשתמש יצר. - הכלל
listמחיל את אותן הגבלות כמוget, אבל על שאילתות. הוא גם בודק את מגבלת השאילתה, ואז דוחה כל שאילתה ללא מגבלה או עם מגבלה שגדולה מ-10. - קבוצת הכללים מגדירה פונקציית
authorOrPublished()כדי למנוע שכפול קוד.
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{storyid} {
// Returns `true` if the requested story is 'published'
// or the user authored the story
function authorOrPublished() {
return resource.data.published == true || request.auth.uid == resource.data.author;
}
// Deny any query not limited to 10 or fewer documents
// Anyone can query published stories
// Authors can query their unpublished stories
allow list: if request.query.limit <= 10 &&
authorOrPublished();
// Anyone can retrieve a published story
// Only a story's author can retrieve an unpublished story
allow get: if authorOrPublished();
// Only a story's author can write to a story
allow write: if request.auth.uid == resource.data.author;
}
}
}
שאילתות של קבוצות אוספים וכללי אבטחה
כברירת מחדל, השאילתות מוגבלות לאוסף אחד והן מאחזרות תוצאות רק מהאוסף הזה. באמצעות שאילתות של קבוצות אוספים, אפשר לאחזר תוצאות מקבוצת אוספים שמורכבת מכל האוספים עם אותו מזהה. בקטע הזה מוסבר איך לאבטח את השאילתות של קבוצת האוספים באמצעות כללי אבטחה.
אבטחת מסמכים ושליחת שאילתות עליהם על סמך קבוצות של אוספים
בכללי האבטחה, צריך לאפשר באופן מפורש שאילתות של קבוצת אוספים על ידי כתיבת כלל לקבוצת האוספים:
- חשוב לוודא שהשורה הראשונה בקובץ הכללים היא
rules_version = '2';. כדי להריץ שאילתות על קבוצות של אוספים, צריך להשתמש בהתנהגות החדשה של התו הכללי הרקורסיבי{name=**}של כללי האבטחה בגרסה 2. - כותבים כלל לקבוצת האוספים באמצעות
match /{path=**}/[COLLECTION_ID]/{doc}.
לדוגמה, נניח שיש פורום שמחולק ל-forum מסמכים שמכילים posts אוספי משנה:
/forums/{forumid}/posts/{postid}
{
author: "some_auth_id",
authorname: "some_username",
content: "I just read a great story.",
}
באפליקציה הזו, אנחנו מאפשרים לבעלים של הפוסטים לערוך אותם, ולמשתמשים מאומתים לקרוא אותם:
service cloud.firestore {
match /databases/{database}/documents {
match /forums/{forumid}/posts/{post} {
// Only authenticated users can read
allow read: if request.auth != null;
// Only the post author can write
allow write: if request.auth != null && request.auth.uid == resource.data.author;
}
}
}
כל משתמש מאומת יכול לאחזר את הפוסטים של כל פורום:
db.collection("forums/technology/posts").get()
אבל מה אם רוצים להציג למשתמש הנוכחי את הפוסטים שלו בכל הפורומים?
אפשר להשתמש בשאילתה של קבוצת אוספים כדי לאחזר תוצאות מכל האוספים של posts:
var user = firebase.auth().currentUser;
db.collectionGroup("posts").where("author", "==", user.uid).get()
בכללי האבטחה, צריך לאפשר את השאילתה הזו על ידי כתיבת כלל קריאה או כלל רשימה לקבוצת האוספים posts:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Authenticated users can query the posts collection group
// Applies to collection queries, collection group queries, and
// single document retrievals
match /{path=**}/posts/{post} {
allow read: if request.auth != null;
}
match /forums/{forumid}/posts/{postid} {
// Only a post's author can write to a post
allow write: if request.auth != null && request.auth.uid == resource.data.author;
}
}
}
עם זאת, חשוב לשים לב שהכללים האלה יחולו על כל האוספים עם המזהה posts, בלי קשר להיררכיה. לדוגמה, הכללים האלה חלים על כל האוספים הבאים של posts:
/posts/{postid}/forums/{forumid}/posts/{postid}/forums/{forumid}/subforum/{subforumid}/posts/{postid}
שאילתות מאובטחות של קבוצות אוספים על סמך שדה
בדומה לשאילתות של אוסף יחיד, גם שאילתות של קבוצת אוספים צריכות לעמוד במגבלות שנקבעו על ידי כללי האבטחה. לדוגמה, אנחנו יכולים להוסיף שדה published לכל פוסט בפורום, כמו שעשינו בדוגמה של stories למעלה:
/forums/{forumid}/posts/{postid}
{
author: "some_auth_id",
authorname: "some_username",
content: "I just read a great story.",
published: false
}
לאחר מכן נוכל לכתוב כללים לקבוצת האוספים posts על סמך הסטטוס published והפוסט author:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Returns `true` if the requested post is 'published'
// or the user authored the post
function authorOrPublished() {
return resource.data.published == true || request.auth.uid == resource.data.author;
}
match /{path=**}/posts/{post} {
// Anyone can query published posts
// Authors can query their unpublished posts
allow list: if authorOrPublished();
// Anyone can retrieve a published post
// Authors can retrieve an unpublished post
allow get: if authorOrPublished();
}
match /forums/{forumid}/posts/{postid} {
// Only a post's author can write to a post
allow write: if request.auth.uid == resource.data.author;
}
}
}
בעזרת הכללים האלה, לקוחות באינטרנט, ב-Apple וב-Android יכולים לבצע את השאילתות הבאות:
כל אחד יכול לאחזר פוסטים שפורסמו בפורום:
db.collection("forums/technology/posts").where('published', '==', true).get()כל אחד יכול לאחזר את הפוסטים שפורסמו על ידי מחבר מסוים בכל הפורומים:
db.collectionGroup("posts").where("author", "==", "some_auth_id").where('published', '==', true).get()מחברים יכולים לאחזר את כל הפוסטים שלהם שפורסמו ושלא פורסמו בכל הפורומים:
var user = firebase.auth().currentUser; db.collectionGroup("posts").where("author", "==", user.uid).get()
אבטחה של מסמכים ושליחת שאילתות עליהם על סמך קבוצת אוספים ונתיב מסמך
במקרים מסוימים, יכול להיות שתרצו להגביל שאילתות של קבוצות אוספים על סמך נתיב המסמך. כדי ליצור את ההגבלות האלה, אתם יכולים להשתמש באותן שיטות לאבטחת מסמכים ולשליחת שאילתות על סמך שדה.
נניח שיש אפליקציה שעוקבת אחרי העסקאות של כל משתמש בכמה בורסות של מניות ומטבעות וירטואליים:
/users/{userid}/exchange/{exchangeid}/transactions/{transaction}
{
amount: 100,
exchange: 'some_exchange_name',
timestamp: April 1, 2019 at 12:00:00 PM UTC-7,
user: "some_auth_id",
}
שימו לב לשדה user. למרות שאנחנו יודעים איזה משתמש הוא הבעלים של מסמך transaction לפי הנתיב של המסמך, אנחנו משכפלים את המידע הזה בכל מסמך transaction כי זה מאפשר לנו לעשות שני דברים:
כתיבת שאילתות של קבוצות אוספים שמוגבלות למסמכים שכוללים
/users/{userid}ספציפי בנתיב המסמך שלהם. לדוגמה:var user = firebase.auth().currentUser; // Return current user's last five transactions across all exchanges db.collectionGroup("transactions").where("user", "==", user).orderBy('timestamp').limit(5)אוכפים את ההגבלה הזו על כל השאילתות בקבוצת האוספים
transactionsכדי שמשתמש אחד לא יוכל לאחזר מסמכיtransactionשל משתמש אחר.
אנחנו אוכפים את ההגבלה הזו בכללי האבטחה שלנו וכוללים אימות נתונים בשדה user:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{path=**}/transactions/{transaction} {
// Authenticated users can retrieve only their own transactions
allow read: if resource.data.user == request.auth.uid;
}
match /users/{userid}/exchange/{exchangeid}/transactions/{transaction} {
// Authenticated users can write to their own transactions subcollections
// Writes must populate the user field with the correct auth id
allow write: if userid == request.auth.uid && request.data.user == request.auth.uid
}
}
}
השלבים הבאים
- במאמר אבטחת הגישה לנתונים של משתמשים וקבוצות מופיעה דוגמה מפורטת יותר לבקרת גישה המבוססת על תפקידים (RBAC).
- קוראים את ההפניה לכללי האבטחה.