תנאי הכתיבה לכללי האבטחה של Cloud Firestore

המדריך הזה מתבסס על המדריך מבנה של כללי אבטחה, ומסביר איך להוסיף תנאים ל-Cloud Firestore Security Rules. אם אתם לא מכירים את העקרונות הבסיסיים של Cloud Firestore Security Rules, כדאי לקרוא את המדריך למתחילים.

אבן הבניין העיקרית של Cloud Firestore Security Rules היא התנאי. תנאי הוא ביטוי בוליאני שקובע אם לאשר או לדחות פעולה מסוימת. אתם יכולים להשתמש בכללי אבטחה כדי לכתוב תנאים שבודקים את אימות המשתמשים, מאמתים נתונים נכנסים או אפילו ניגשים לחלקים אחרים במסד הנתונים.

כדי לקבל עזרה מ-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.

אימות

אחד מהדפוסים הנפוצים ביותר של כללי אבטחה הוא שליטה בגישה על סמך מצב האימות של המשתמש. לדוגמה, יכול להיות שתרצו לאפשר רק למשתמשים מחוברים לכתוב נתונים באפליקציה:

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to access documents in the "cities" collection
    // only if they are authenticated.
    match /cities/{city} {
      allow read, write: if request.auth != null;
    }
  }
}

תבנית נפוצה נוספת היא לוודא שמשתמשים יכולים לקרוא ולכתוב רק את הנתונים שלהם:

service cloud.firestore {
  match /databases/{database}/documents {
    // Make sure the uid of the requesting user matches name of the user
    // document. The wildcard expression {userId} makes the userId variable
    // available in rules.
    match /users/{userId} {
      allow read, update, delete: if request.auth != null && request.auth.uid == userId;
      allow create: if request.auth != null;
    }
  }
}

אם האפליקציה משתמשת באימות ב-Firebase או ב-Google Cloud Identity Platform, המשתנה request.auth מכיל את פרטי האימות של הלקוח שמבקש נתונים. מידע נוסף על request.auth זמין במאמרי העזרה.

אימות נתונים

באפליקציות רבות, פרטי בקרת הגישה מאוחסנים כשדות במסמכים במסד הנתונים. ‫Cloud Firestore Security Rules יכול לאפשר או לדחות גישה באופן דינמי על סמך נתוני המסמך:

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to read data if the document has the 'visibility'
    // field set to 'public'
    match /cities/{city} {
      allow read: if resource.data.visibility == 'public';
    }
  }
}

המשתנה resource מתייחס למסמך המבוקש, ו-resource.data הוא מפה של כל השדות והערכים שמאוחסנים במסמך. למידע נוסף על המשתנה resource, קראו את מאמרי העזרה.

כשכותבים נתונים, יכול להיות שתרצו להשוות בין הנתונים הנכנסים לבין הנתונים הקיימים. במקרה כזה, אם קבוצת הכללים מאפשרת את הכתיבה בהמתנה, המשתנה request.resource מכיל את המצב העתידי של המסמך. בפעולות update שמשנות רק קבוצת משנה של שדות המסמך, המשתנה request.resource יכיל את מצב המסמך בהמתנה אחרי הפעולה. כדי למנוע עדכוני נתונים לא רצויים או לא עקביים, אתם יכולים לבדוק את ערכי השדות ב-request.resource:

service cloud.firestore {
  match /databases/{database}/documents {
    // Make sure all cities have a positive population and
    // the name is not changed
    match /cities/{city} {
      allow update: if request.resource.data.population > 0
                    && request.resource.data.name == resource.data.name;
    }
  }
}

גישה למסמכים אחרים

באמצעות הפונקציות get() ו-exists(), כללי האבטחה יכולים להעריך בקשות נכנסות בהשוואה למסמכים אחרים במסד הנתונים. הפונקציות get() ו-exists() מצפות לנתיבי מסמכים שצוינו במלואם. כשמשתמשים במשתנים כדי ליצור נתיבים ל-get() ול-exists(), צריך להוסיף תווי בריחה (escape) למשתנים באופן מפורש באמצעות התחביר $(variable).

בדוגמה הבאה, המשתנה database מתועד על ידי הצהרת ההתאמה match /databases/{database}/documents ומשמש ליצירת הנתיב:

service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city} {
      // Make sure a 'users' document exists for the requesting user before
      // allowing any writes to the 'cities' collection
      allow create: if request.auth != null && exists(/databases/$(database)/documents/users/$(request.auth.uid));

      // Allow the user to delete cities if their user document has the
      // 'admin' field set to 'true'
      allow delete: if request.auth != null && get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true;
    }
  }
}

לפעולות כתיבה, אפשר להשתמש בפונקציה getAfter() כדי לגשת למצב של מסמך אחרי שטרנזקציה או פעולת כתיבה באצווה הושלמו, אבל לפני שהטרנזקציה או האצווה בוצעו. בדומה לפונקציה get(), הפונקציה getAfter() מקבלת נתיב מסמך שצוין במלואו. אפשר להשתמש ב-getAfter() כדי להגדיר קבוצות של פעולות כתיבה שצריכות להתבצע יחד כטרנזקציה או כקבוצה.

מגבלות על קריאות גישה

יש מגבלה על מספר הקריאות לגישה למסמכים בכל הערכה של קבוצת כללים:

  • ‫10 לבקשות של מסמך יחיד ולבקשות של שאילתות.
  • ‫20 לפעולות קריאה של כמה מסמכים, טרנזקציות וכתיבה באצווה. המגבלה הקודמת של 10 חלה גם על כל פעולה.

    לדוגמה, נניח שאתם יוצרים בקשת כתיבה באצווה עם 3 פעולות כתיבה, וכללי האבטחה שלכם משתמשים ב-2 קריאות גישה למסמכים כדי לאמת כל כתיבה. במקרה הזה, כל פעולת כתיבה משתמשת ב-2 מתוך 10 קריאות הגישה שלה, ובקשת הכתיבה באצווה משתמשת ב-6 מתוך 20 קריאות הגישה שלה.

אם חורגים מאחת מהמגבלות, מתקבלת הודעת שגיאה על דחיית הרשאה. יכול להיות שחלק מהקריאות לגישה למסמכים יישמרו במטמון, וקריאות שנשמרות במטמון לא נספרות במגבלות.

הסבר מפורט על האופן שבו המגבלות האלה משפיעות על טרנזקציות ועל כתיבה באצווה זמין במדריך בנושא אבטחת פעולות אטומיות.

גישה לשיחות ולתמחור

השימוש בפונקציות האלה מפעיל פעולת קריאה במסד הנתונים, כלומר תחויבו על קריאת מסמכים גם אם הכללים שלכם דוחים את הבקשה. למידע ספציפי יותר על חיוב, קראו את המאמר תמחור כללים.

פונקציות מותאמות אישית

ככל שכללי האבטחה הופכים למורכבים יותר, יכול להיות שתרצו לעטוף קבוצות של תנאים בפונקציות שתוכלו להשתמש בהן שוב בקבוצת הכללים. כללי אבטחה תומכים בפונקציות בהתאמה אישית. התחביר של פונקציות בהתאמה אישית דומה קצת ל-JavaScript, אבל פונקציות של כללי אבטחה נכתבות בשפה ספציפית לדומיין, שיש לה כמה מגבלות חשובות:

  • פונקציות יכולות להכיל רק הצהרת return אחת. הם לא יכולים להכיל לוגיקה נוספת. לדוגמה, הם לא יכולים להריץ לולאות או לקרוא לשירותים חיצוניים.
  • פונקציות יכולות לגשת באופן אוטומטי לפונקציות ולמשתנים מהטווח שבו הן מוגדרות. לדוגמה, לפונקציה שמוגדרת בהיקף service cloud.firestore יש גישה למשתנה resource ולפונקציות מובנות כמו get() ו-exists().
  • פונקציות יכולות לקרוא לפונקציות אחרות, אבל לא יכולות לבצע רקורסיה. העומק הכולל של סטאק הביצוע מוגבל ל-10.
  • בגרסה v2 של הכללים, פונקציות יכולות להגדיר משתנים באמצעות מילת המפתח let. פונקציות יכולות לכלול עד 10 קישורי let, אבל הן חייבות להסתיים בפקודת חזרה.

פונקציה מוגדרת באמצעות מילת המפתח function, והיא מקבלת אפס ארגומנטים או יותר. לדוגמה, יכול להיות שתרצו לשלב את שני סוגי התנאים שבהם השתמשנו בדוגמאות שלמעלה לפונקציה אחת:

service cloud.firestore {
  match /databases/{database}/documents {
    // True if the user is signed in or the requested data is 'public'
    function signedInOrPublic() {
      return request.auth.uid != null || resource.data.visibility == 'public';
    }

    match /cities/{city} {
      allow read, write: if signedInOrPublic();
    }

    match /users/{user} {
      allow read, write: if signedInOrPublic();
    }
  }
}

ככל שכללי האבטחה הופכים למורכבים יותר, השימוש בפונקציות בכללים האלה מאפשר לתחזק אותם בצורה טובה יותר.

כללים הם לא מסננים

אחרי שמאבטחים את הנתונים ומתחילים לכתוב שאילתות, חשוב לזכור שכללי האבטחה הם לא מסננים. אי אפשר לכתוב שאילתה לכל המסמכים באוסף ולצפות שהכללים יחזירו רק את המסמכים שללקוח הנוכחי יש הרשאה לגשת אליהם.

לדוגמה, נניח שזהו כלל האבטחה:

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to read data if the document has the 'visibility'
    // field set to 'public'
    match /cities/{city} {
      allow read: if resource.data.visibility == 'public';
    }
  }
}

נדחתה: הכלל הזה דוחה את השאילתה הבאה כי קבוצת התוצאות יכולה לכלול מסמכים שבהם הערך של visibility הוא לא public:

אתר
db.collection("cities").get()
    .then(function(querySnapshot) {
        querySnapshot.forEach(function(doc) {
            console.log(doc.id, " => ", doc.data());
    });
});

מותר: הכלל הזה מאפשר את השאילתה הבאה כי סעיף ה-where("visibility", "==", "public") מבטיח שקבוצת התוצאות עומדת בתנאי של הכלל:

אתר
db.collection("cities").where("visibility", "==", "public").get()
    .then(function(querySnapshot) {
        querySnapshot.forEach(function(doc) {
            console.log(doc.id, " => ", doc.data());
        });
    });

כללי האבטחה של Rules מעריכים כל שאילתה ביחס לתוצאה הפוטנציאלית שלה, ומכשילים את הבקשה אם היא יכולה להחזיר מסמך שללקוח אין הרשאה לקרוא. השאילתות חייבות לעמוד במגבלות שנקבעו בכללי האבטחה. למידע נוסף על כללי אבטחה ושאילתות, ראו שאילתות מאובטחות של נתונים.

השלבים הבאים