המדריך הזה מבוסס על המדריך בנושא מבנה של כללי אבטחה. במדריך הזה מוסבר איך להוסיף תנאים ל-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()
, צריך להשתמש בתחביר $(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 קריאות הגישה שלה.
חריגה מאחת מהמגבלות האלה תגרום לשגיאה 'ההרשאה נדחתה'. יכול להיות שחלק מהקריאות לגישה למסמכים יישמרו במטמון, והקריאות האלה לא ייספרו במגבלות.
הסבר מפורט על האופן שבו המגבלות האלה משפיעות על טרנזקציות ועל כתיבות באצ' זמין במדריך בנושא אבטחת פעולות אטומיות.
גישה לשיחות ולתמחור
השימוש בפונקציות האלה מפעיל פעולת קריאה במסד הנתונים, כלומר תחויבו על קריאת מסמכים גם אם הכללים דוחים את הבקשה. מידע ספציפי יותר על חיוב זמין במאמר Cloud Firestoreתמחור.
פונקציות מותאמות אישית
ככל שכללי האבטחה הופכים מורכבים יותר, כדאי להשתמש בפונקציות שאפשר לעשות בהן שימוש חוזר בכללי האבטחה. כללי אבטחה תומכים בפונקציות בהתאמה אישית. התחביר של פונקציות בהתאמה אישית דומה ל-JavaScript, אבל פונקציות של כללי אבטחה נכתבות בשפה ספציפית לתחום, שיש לה כמה מגבלות חשובות:
- פונקציות יכולות להכיל רק הצהרת
return
אחת. הם לא יכולים להכיל לוגיקה נוספת. לדוגמה, הם לא יכולים להריץ לולאות או לקרוא לשירותים חיצוניים. - פונקציות יכולות לגשת אוטומטית לפונקציות ולמשתנים מההיקף שבו הן מוגדרות. לדוגמה, לפונקציה שמוגדרת בהיקף
service cloud.firestore
יש גישה למשתנהresource
ולפונקציות מובנות כמוget()
ו-exists()
. - פונקציות יכולות לקרוא לפונקציות אחרות, אבל לא יכולות לקרוא לעצמן. העומק הכולל של מחסנית הקריאות מוגבל ל-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 כל שאילתה נבדקת על ידי כללי האבטחה מול התוצאה הפוטנציאלית שלה, והבקשה נדחית אם היא עלולה להחזיר מסמך שללקוח אין הרשאה לקרוא. השאילתות צריכות לעמוד במגבלות שנקבעו בכללי האבטחה. מידע נוסף על כללי אבטחה ושאילתות זמין במאמר בנושא שאילתות מאובטחות של נתונים.
השלבים הבאים
- איך כללי אבטחה משפיעים על השאילתות
- איך לבנות כללי אבטחה
- לקרוא את ההפניה לכללי אבטחה.
- אם האפליקציות שלכם משתמשות ב-Cloud Storage for Firebase, תוכלו לקרוא איך לכתוב תנאי Cloud Storage Security Rules לגישה למסמכי Cloud Firestore.