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

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

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

הצהרה על שירות ומסד נתונים

Cloud Firestore Security Rules תמיד מתחילים בהצהרה הבאה:

service cloud.firestore {
  match /databases/{database}/documents {
    // ...
  }
}

ההצהרה service cloud.firestore מגדירה את היקף הכללים ל-Cloud Firestore, וכך מונעת התנגשויות בין Cloud Firestore Security Rules לבין כללים של מוצרים אחרים, כמו Cloud Storage.

ההצהרה match /databases/{database}/documents מציינת שהכללים צריכים להתאים לכל מסד נתונים Cloud Firestore בפרויקט. בשלב הזה, לכל פרויקט יש רק מסד נתונים אחד בשם (default).

כללי קריאה/כתיבה בסיסיים

כללים בסיסיים מורכבים מהצהרת match שמציינת נתיב למסמך ומביטוי allow שמפרט מתי מותר לקרוא את הנתונים שצוינו:

service cloud.firestore {
  match /databases/{database}/documents {

    // Match any document in the 'cities' collection
    match /cities/{city} {
      allow read: if <condition>;
      allow write: if <condition>;
    }
  }
}

כל הצהרות ההתאמה צריכות להפנות למסמכים, ולא לאוספים. הצהרת התאמה יכולה להפנות למסמך ספציפי, כמו בדוגמה match /cities/SF, או להשתמש בתווים כלליים כדי להפנות לכל מסמך בנתיב שצוין, כמו בדוגמה match /cities/{city}.

בדוגמה שלמעלה, משפט ההתאמה משתמש בתחביר של התו הכללי {city}. המשמעות היא שהכלל חל על כל מסמך באוסף cities, כמו /cities/SF או /cities/NYC. כשמבצעים הערכה של הביטויים allow בהצהרת ההתאמה, המשתנה city יומר לשם המסמך של העיר, כמו SF או NYC.

פעולות פרטניות

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

אפשר לפצל כלל read לכללים get ו-list, ואפשר לפצל כלל write לכללים create,‏ update ו-delete:

service cloud.firestore {
  match /databases/{database}/documents {
    // A read rule can be divided into get and list rules
    match /cities/{city} {
      // Applies to single document read requests
      allow get: if <condition>;

      // Applies to queries and collection read requests
      allow list: if <condition>;
    }

    // A write rule can be divided into create, update, and delete rules
    match /cities/{city} {
      // Applies to writes to nonexistent documents
      allow create: if <condition>;

      // Applies to writes to existing documents
      allow update: if <condition>;

      // Applies to delete operations
      allow delete: if <condition>;
    }
  }
}

נתונים היררכיים

הנתונים ב-Cloud Firestore מאורגנים באוספים של מסמכים, וכל מסמך יכול להרחיב את ההיררכיה באמצעות אוספי משנה. חשוב להבין איך כללי האבטחה פועלים עם נתונים היררכיים.

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

service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city} {
      allow read, write: if <condition>;

        // Explicitly define rules for the 'landmarks' subcollection
        match /landmarks/{landmark} {
          allow read, write: if <condition>;
        }
    }
  }
}

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

service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city} {
      match /landmarks/{landmark} {
        allow read, write: if <condition>;
      }
    }
  }
}
service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city}/landmarks/{landmark} {
      allow read, write: if <condition>;
    }
  }
}

תווים כלליים לחיפוש רקורסיבי

אם רוצים שהכללים יחולו על היררכיה בעומק שרירותי, צריך להשתמש בתחביר של תו כללי לחיפוש רקורסיבי, {name=**}. לדוגמה:

service cloud.firestore {
  match /databases/{database}/documents {
    // Matches any document in the cities collection as well as any document
    // in a subcollection.
    match /cities/{document=**} {
      allow read, write: if <condition>;
    }
  }
}

כשמשתמשים בתחביר של תו כללי לחיפוש רקורסיבי, משתנה התו הכללי יכיל את כל פלח הנתיב התואם, גם אם המסמך נמצא באוסף משנה עם קינון עמוק. לדוגמה, הכללים שמופיעים למעלה יתאימו למסמך שנמצא בכתובת /cities/SF/landmarks/coit_tower, והערך של המשתנה document יהיה SF/landmarks/coit_tower.

עם זאת, חשוב לזכור שההתנהגות של תווים כלליים רקורסיביים תלויה בגרסת הכללים.

גרסה 1

כללי האבטחה משתמשים בגרסה 1 כברירת מחדל. בגרסה 1, תווים כלליים רקורסיביים תואמים לפריט נתיב אחד או יותר. הם לא תואמים לנתיב ריק, ולכן match /cities/{city}/{document=**} תואם למסמכים באוספי משנה אבל לא באוסף cities, ואילו match /cities/{document=**} תואם למסמכים באוסף cities ובאוספי משנה.

תווים כלליים רקורסיביים חייבים להופיע בסוף הצהרת התאמה.

גרסה 2

בגרסה 2 של כללי האבטחה, תווים כלליים לחיפוש רקורסיביים תואמים לאפס פריטים או יותר בנתיב. ‫match/cities/{city}/{document=**} תואם למסמכים בכל אוסף משנה וגם למסמכים באוסף cities.

כדי להצטרף לגרסה 2, צריך להוסיף את rules_version = '2'; לחלק העליון של כללי האבטחה:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Matches any document in the cities collection as well as any document
    // in a subcollection.
    match /cities/{city}/{document=**} {
      allow read, write: if <condition>;
    }
  }
}

יכול להיות לכם לכל היותר תו כללי רקורסיבי אחד בכל הצהרת התאמה, אבל בגרסה 2, אתם יכולים למקם את התו הכללי הזה בכל מקום בהצהרת ההתאמה. לדוגמה:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Matches any document in the songs collection group
    match /{path=**}/songs/{song} {
      allow read, write: if <condition>;
    }
  }
}

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

הצהרות חופפות של התאמה

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

service cloud.firestore {
  match /databases/{database}/documents {
    // Matches any document in the 'cities' collection.
    match /cities/{city} {
      allow read, write: if false;
    }

    // Matches any document in the 'cities' collection or subcollections.
    match /cities/{document=**} {
      allow read, write: if true;
    }
  }
}

בדוגמה שלמעלה, כל פעולות הקריאה והכתיבה באוסף cities יותרו כי הכלל השני הוא תמיד true, גם אם הכלל הראשון הוא תמיד false.

מגבלות על כללי אבטחה

כשעובדים עם כללי אבטחה, חשוב לשים לב למגבלות הבאות:

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

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

חריגה מאחת מהמגבלות האלה תגרום לשגיאה 'ההרשאה נדחתה'.

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

העומק המקסימלי של משפט match מקונן 10
אורך הנתיב המקסימלי, בפלחי נתיב, שמותר בתוך קבוצה של match הצהרות מקוננות 100
המספר המקסימלי של משתני לכידת נתיבים שמותר להשתמש בהם בתוך קבוצה של match הצהרות מקוננות 20
עומק מקסימלי של בקשות להפעלת פונקציה 20
מספר הארגומנטים המקסימלי של הפונקציה 7
מספר מקסימלי של let קשירות משתנים לכל פונקציה 10
המספר המקסימלי של קריאות לפונקציות רקורסיביות או מחזוריות ‫0 (אין הרשאה)
המספר המקסימלי של ביטויים שמוערכים לכל בקשה 1,000
הגודל המקסימלי של קבוצת כללים יש שני מגבלות גודל שחלות על קבוצות כללים:
  • הגבלה של 256KB על הגודל של מקור הטקסט של קבוצת הכללים שפורסמה ממסוף Firebase או מ-CLI באמצעות firebase deploy.
  • מגבלה של 250KB על הגודל של קבוצת הכללים המהודרת שמתקבלת כש-Firebase מעבד את המקור והופך אותו לפעיל בשרת העורפי.

השלבים הבאים