המדריך הזה מבוסס על המדריך לימוד התחביר הבסיסי של שפת Firebase Security Rules, ומראה איך להוסיף תנאים ל-Firebase Security Rules של Cloud Storage.
אבן הבניין הראשית של Cloud Storage Security Rules הוא התנאי. תנאי הוא ביטוי בוליאני שקובע אם יש לאפשר או לדחות פעולה מסוימת. בכלל בסיסי, השימוש ב-literals של true
ו-false
בתור תנאים עובד מצוין. אבל השפה Firebase Security Rules for Cloud Storage מאפשרת לכתוב תנאים מורכבים יותר, שיכולים:
- בדיקת אימות המשתמש
- אימות הנתונים הנכנסים
אימות
Firebase Security Rules ל-Cloud Storage משתלב עם Firebase Authentication כדי לספק ל-Cloud Storage אימות מתקדם שמבוסס על משתמשים. כך אפשר לבצע בקרת גישה מפורטת על סמך הצהרות של אסימון Firebase Authentication.
כשמשתמש מאומת מבצע בקשה נגד Cloud Storage, משתנה request.auth
מאוכלס ב-uid
של המשתמש (request.auth.uid
) וגם בהצהרות של Firebase Authentication JWT (request.auth.token
).
בנוסף, כשמשתמשים באימות מותאם אישית, הצהרות נוספות מופיעות בשדה request.auth.token
.
כשמשתמש לא מאומת מבצע בקשה, המשתנה request.auth
הוא null
.
בעזרת הנתונים האלה, יש כמה דרכים נפוצות להשתמש באימות כדי לאבטח את הקבצים:
- ציבורי: התעלמות מ-
request.auth
- פרטי מאומת: בודקים ש-
request.auth
אינוnull
- פרטי משתמש: בודקים ש-
request.auth.uid
שווה לנתיבuid
- פרטי קבוצה: בודקים את ההצהרות של האסימון בהתאמה אישית כדי להתאים להצהרה שנבחרה, או קוראים את המטא-נתונים של הקובץ כדי לראות אם קיים שדה מטא-נתונים
גלוי לכולם
כל כלל שלא מביא בחשבון את ההקשר של request.auth
יכול להיחשב לכלל public
, כי הוא לא מביא בחשבון את הקשר האימות של המשתמש.
הכללים האלה יכולים להיות שימושיים כדי להציג נתונים ציבוריים, כמו נכסי משחקים, קובצי קול או תוכן סטטי אחר.
// Anyone to read a public image if the file is less than 100kB // Anyone can upload a public file ending in '.txt' match /public/{imageId} { allow read: if resource.size < 100 * 1024; allow write: if imageId.matches(".*\\.txt"); }
פרטי מאומת,
במקרים מסוימים, יכול להיות שתרצו שכל המשתמשים המאומתים באפליקציה יוכלו לראות את הנתונים, אבל לא משתמשים לא מאומתים. מכיוון שהמשתנה request.auth
הוא null
לכל המשתמשים הלא מאומתים, כל מה שצריך לעשות הוא לבדוק שהמשתנה request.auth
קיים כדי לדרוש אימות:
// Require authentication on all internal image reads match /internal/{imageId} { allow read: if request.auth != null; }
פרטי למשתמש
התרחיש הנפוץ ביותר לשימוש ב-request.auth
הוא מתן הרשאות מפורטות על הקבצים של משתמשים ספציפיים: החל מהעלאת תמונות פרופיל ועד לקריאת מסמכים פרטיים.
מכיוון שלקבצים ב-Cloud Storage יש 'נתיב' מלא לקובץ, כל מה שצריך כדי ליצור קובץ שמנוהל על ידי משתמש הוא קטע פרטים ייחודי וייחודי לזיהוי המשתמש בקידומת שם הקובץ (למשל, uid
של המשתמש). כדי לבדוק את הכלל, צריך לבדוק את הפרטים האלה:
// Only a user can upload their profile picture, but anyone can view it match /users/{userId}/profilePicture.png { allow read; allow write: if request.auth.uid == userId; }
קבוצה פרטית
תרחיש לדוגמה נוסף, שגם הוא נפוץ מאוד, הוא מתן הרשאות קבוצתיות לאובייקט, למשל מתן הרשאה לכמה חברי צוות לשתף פעולה במסמך משותף. יש כמה גישות לביצוע הפעולה הזו:
- יצירה של אסימון בהתאמה אישית של Firebase Authentication שמכיל מידע נוסף על חבר בקבוצה (למשל מזהה קבוצה)
- כוללים את פרטי הקבוצה (כמו מזהה קבוצה או רשימה של פריטי
uid
מורשים) במטא-נתונים של הקובץ
אחרי שהנתונים האלה מאוחסנים במטא-נתונים של האסימון או הקובץ, אפשר להפנות אליהם מתוך כלל:
// Allow reads if the group ID in your token matches the file metadata's `owner` property // Allow writes if the group ID is in the user's custom token match /files/{groupId}/{fileName} { allow read: if resource.metadata.owner == request.auth.token.groupId; allow write: if request.auth.token.groupId == groupId; }
שליחת בקשה להערכה
העלאות, הורדות, שינויים במטא-נתונים ומחיקות מוערכות באמצעות הערך request
שנשלח אל Cloud Storage. בנוסף למזהה הייחודי של המשתמש ולמטען הייעודי Firebase Authentication באובייקט request.auth
כפי שמתואר למעלה, המשתנה request
מכיל את נתיב הקובץ שבו מתבצעת הבקשה, את השעה שבה התקבלה הבקשה ואת הערך החדש של resource
אם הבקשה היא לכתיבה.
האובייקט request
מכיל גם את המזהה הייחודי של המשתמש ואת עומס העבודה (payload) של Firebase Authentication באובייקט request.auth
. מידע נוסף זמין בקטע אבטחה מבוססת-משתמשים במסמכים.
בהמשך מופיעה רשימה מלאה של המאפיינים באובייקט request
:
נכס | סוג | תיאור |
---|---|---|
auth |
map<string, string> | כשמשתמש מחובר לחשבון, הוא מספק את uid , המזהה הייחודי של המשתמש, וגם את token , מפה של Firebase Authentication הצהרות JWT. אחרת, הערך יהיה null . |
params |
מפה<string, string> | מפה שמכילה את הפרמטרים של השאילתה של הבקשה. |
path |
נתיב | path שמייצג את הנתיב שבו מתבצעת הבקשה. |
resource |
map<string, string> | ערך המשאב החדש, שמופיע רק בבקשות write .
|
time |
חותמת זמן | חותמת זמן שמייצגת את הזמן שבו מתבצעת בדיקת הבקשה בשרת. |
הערכת משאבים
כשבודקים את הכללים, כדאי גם לבדוק את המטא-נתונים של הקובץ שרוצים להעלות, להוריד, לשנות או למחוק. כך תוכלו ליצור כללים מורכבים וחזקים, כמו לאפשר העלאה רק של קבצים עם סוגים מסוימים של תוכן, או מחיקה רק של קבצים גדולים מגודל מסוים.
Firebase Security Rules ל-Cloud Storage מספק מטא-נתונים של קבצים באובייקט resource
, שמכיל צמדי מפתח/ערך של המטא-נתונים שמופיעים באובייקט Cloud Storage. אפשר לבדוק את המאפיינים האלה בבקשות read
או write
כדי להבטיח את תקינות הנתונים.
בבקשות write
(כמו העלאות, עדכוני מטא-נתונים ומחיקה), בנוסף לאובייקט resource
שמכיל את המטא-נתונים של הקובץ שקיים כרגע בנתיב הבקשה, יש לכם גם אפשרות להשתמש באובייקט request.resource
שמכיל קבוצת משנה של המטא-נתונים של הקובץ שרוצים לכתוב אם הכתיבה מותרת. אפשר להשתמש בשני הערכים האלה כדי להבטיח את תקינות הנתונים או כדי לאכוף אילוצים של אפליקציות כמו סוג או גודל הקובץ.
בהמשך מופיעה רשימה מלאה של המאפיינים באובייקט resource
:
נכס | סוג | תיאור |
---|---|---|
name |
מחרוזת | השם המלא של האובייקט |
bucket |
מחרוזת | השם של הקטגוריה שבה האובייקט נמצא. |
generation |
int | יצירת האובייקט Google Cloud Storage של האובייקט הזה. |
metageneration |
int | המטא-גנרציה של האובייקט Google Cloud Storage של האובייקט הזה. |
size |
int | גודל האובייקט בבייטים. |
timeCreated |
חותמת זמן | חותמת זמן שמייצגת את השעה שבה האובייקט נוצר. |
updated |
חותמת זמן | חותמת זמן שמייצגת את הזמן שבו אובייקט עודכן בפעם האחרונה. |
md5Hash |
מחרוזת | גיבוב (hash) MD5 של האובייקט. |
crc32c |
מחרוזת | גיבוב CRC32C של האובייקט. |
etag |
מחרוזת | ה-etag שמשויך לאובייקט הזה. |
contentDisposition |
מחרוזת | צורת התוכן שמשויכת לאובייקט הזה. |
contentEncoding |
מחרוזת | קידוד התוכן שמשויך לאובייקט הזה. |
contentLanguage |
מחרוזת | שפת התוכן שמשויכת לאובייקט הזה. |
contentType |
מחרוזת | סוג התוכן שמשויך לאובייקט הזה. |
metadata |
מפה<string, string> | צמדי מפתח/ערך של מטא-נתונים מותאמים אישית נוספים שצוינו על ידי המפתח. |
request.resource
מכיל את כל אלה, מלבד generation
, metageneration
, etag
, timeCreated
ו-updated
.
שיפור באמצעות Cloud Firestore
אפשר לגשת למסמכים ב-Cloud Firestore כדי לבדוק קריטריונים אחרים להרשאות.
באמצעות הפונקציות firestore.get()
ו-firestore.exists()
, כללי האבטחה יכולים להעריך בקשות נכנסות מול מסמכים ב-Cloud Firestore.
הפונקציות firestore.get()
ו-firestore.exists()
מצפות לנתיבי מסמכים שצוינו במלואם. כשמשתמשים במשתנים כדי ליצור נתיב ל-firestore.get()
ול-firestore.exists()
, צריך להשתמש בתחביר $(variable)
כדי להימלט מהם באופן מפורש.
בדוגמה הבאה מוצג כלל שמגביל את הגישה לקריאת קבצים למשתמשים שהם חברים במועדונים מסוימים.
service firebase.storage { match /b/{bucket}/o { match /users/{club}/files/{fileId} { allow read: if club in firestore.get(/databases/(default)/documents/users/$(request.auth.id)).memberships } } }
service firebase.storage { match /b/{bucket}/o { match /users/{userId}/photos/{fileId} { allow read: if firestore.exists(/databases/(default)/documents/users/$(userId)/friends/$(request.auth.id)) } } }
אחרי שיוצרים ושומרים את ה-Cloud Storage Security Rules הראשון שמשתמש בפונקציות האלה של Cloud Firestore, מופיעה בקשה במסוף Firebase או ב-CLI של Firebase להפעיל הרשאות כדי לחבר את שני המוצרים.
כדי להשבית את התכונה, מסירים תפקיד IAM, כפי שמתואר בקטע ניהול ופריסה של Firebase Security Rules.
אימות נתונים
אפשר להשתמש ב-Firebase Security Rules ל-Cloud Storage גם לאימות נתונים, כולל אימות של שם הקובץ והנתיב, וגם מאפיינים של מטא-נתונים של קבצים כמו contentType
ו-size
.
service firebase.storage { match /b/{bucket}/o { match /images/{imageId} { // Only allow uploads of any image file that's less than 5MB allow write: if request.resource.size < 5 * 1024 * 1024 && request.resource.contentType.matches('image/.*'); } } }
פונקציות מותאמות אישית
ככל ש-Firebase Security Rules נעשה מורכב יותר, כדאי לתחום קבוצות של תנאים בפונקציות שאפשר לעשות בהן שימוש חוזר בכל כללי המדיניות. כללי האבטחה תומכים בפונקציות בהתאמה אישית. התחביר לפונקציות מותאמות אישית דומה ב-JavaScript, אבל פונקציות Firebase Security Rules נכתבות בשפה ספציפית לדומיין שיש לה כמה מגבלות חשובות:
- פונקציות יכולות להכיל רק הצהרת
return
אחת. הן לא יכולות להכיל לוגיקה נוספת. לדוגמה, הם לא יכולים להריץ לולאות או לבצע קריאה לשירותים חיצוניים. - פונקציות יכולות לגשת באופן אוטומטי לפונקציות ולמשתנים מההיקף שבו הן מוגדרות. לדוגמה, לפונקציה שמוגדרת בהיקף
service firebase.storage
יש גישה למשתנהresource
, ולפונקציות מובנות כמוget()
ו-exists()
רק בהיקף Cloud Firestore. - פונקציות יכולות לקרוא לפונקציות אחרות, אבל אסור להן לבצע חזרה חוזרת (recursion). העומק הכולל של מקבץ הקריאות מוגבל ל-10.
- בגרסה
rules2
, אפשר להגדיר משתנים בפונקציות באמצעות מילת המפתחlet
. פונקציות יכולות לכלול כל מספר קישורי let, אבל הן חייבות להסתיים בהצהרת return.
פונקציה מוגדרת עם מילת המפתח function
ומקבלת אפס ארגומנטים או יותר. לדוגמה, אפשר לשלב את שני סוגי התנאים שבדוגמאות שלמעלה בפונקציה אחת:
service firebase.storage {
match /b/{bucket}/o {
// 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 /images/{imageId} {
allow read, write: if signedInOrPublic();
}
match /mp3s/{mp3Ids} {
allow read: if signedInOrPublic();
}
}
}
השימוש בפונקציות ב-Firebase Security Rules מקל על תחזוקת הכללים ככל שהמורכבות של הכללים גדלה.
השלבים הבאים
אחרי הדיון הזה על תנאים, יש לכם הבנה מעמיקה יותר של כללים ואתם מוכנים:
איך מטפלים בתרחישי לדוגמה מרכזיים, ומה תהליך העבודה לפיתוח, בדיקה ופריסה של כללים:
- כותבים כללים שמותאמים לתרחישים נפוצים.
- כדי להרחיב את הידע שלכם, כדאי לקרוא על מצבים שבהם צריך לזהות כללים לא מאובטחים ולהימנע מהם.
- בדיקת הכללים באמצעות המכונה הווירטואלית Cloud Storage וספריית הבדיקה הייעודית של כללי האבטחה.
- אפשר לעיין בשיטות הזמינות לפריסה של Rules.