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

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

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

אימות

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

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 מתוך 10 קריאות הגישה שלה, ובבקשת הכתיבה באצווה נעשה שימוש ב-6 מתוך 20 קריאות הגישה שלה.

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

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

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

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

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

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

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

פונקציה מוגדרת באמצעות מילת המפתח 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();
    }
  }
}

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

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

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

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

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());
        });
    });

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

השלבים הבאים