שימוש בתנאים בכללי אבטחה של Cloud Storage ב-Firebase

המדריך הזה מבוסס על המדריך לומדים את תחביר הליבה של שפת Firebase Security Rules. במדריך הזה מוסבר איך להוסיף תנאים ל-Firebase Security Rules עבור Cloud Storage.

אבן הבניין העיקרית של Cloud Storage Security Rules היא התנאי. תנאי הוא ביטוי בוליאני שקובע אם פעולה מסוימת צריכה להיות מותרת או אסורה. בכללים בסיסיים, שימוש במחרוזות true ו-false כמחרוזות מילוליות בתנאים עובד בצורה מושלמת. אבל Firebase Security Rules בשפת Cloud Storage מאפשר לכם לכתוב תנאים מורכבים יותר שיכולים:

  • בדיקת אימות משתמשים
  • אימות נתונים נכנסים

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

אימות

‫Firebase Security Rules ל-Cloud Storage משתלב עם Firebase Authentication כדי לספק אימות עוצמתי מבוסס-משתמש ל-Cloud Storage. כך אפשר להגדיר בקרת גישה מפורטת על סמך טענות של אסימון Firebase Authentication.

כשמשתמש מאומת שולח בקשה אל Cloud Storage, המשתנה request.auth מאוכלס בuid של המשתמש (request.auth.uid) וגם בהצהרות של אסימון ה-JWT‏ Firebase Authentication (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;
}

קבוצה פרטית

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

אחרי שהנתונים האלה מאוחסנים במטא-נתונים של האסימון או הקובץ, אפשר להפנות אליהם מתוך כלל:

// 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. בנוסף למזהה הייחודי של המשתמש ולמטען הייעודי (payload) Firebase Authentication באובייקט request.auth, כפי שמתואר למעלה, המשתנה request מכיל את נתיב הקובץ שבו מתבצעת הבקשה, את השעה שבה הבקשה מתקבלת ואת הערך החדש resource אם הבקשה היא בקשת כתיבה.

אובייקט request מכיל גם את המזהה הייחודי של המשתמש ואת מטען הייעודי (payload) של Firebase Authentication באובייקט request.auth, שיוסבר בהמשך בקטע אבטחה מבוססת-משתמש במסמכים.

בהמשך מופיעה רשימה מלאה של המאפיינים באובייקט request:

נכס סוג תיאור
auth map<string, string> כשמשתמש מחובר לחשבון, המערכת מספקת את uid, המזהה הייחודי של המשתמש, ואת token, מיפוי של הצהרות JWT של Firebase Authentication. אחרת, הוא יהיה null.
params map<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 מחרוזת גיבוב MD5 של האובייקט.
crc32c מחרוזת גיבוב (hash) מסוג crc32c של האובייקט.
etag מחרוזת תג ה-etag שמשויך לאובייקט הזה.
contentDisposition מחרוזת המאפיין content-disposition שמשויך לאובייקט הזה.
contentEncoding מחרוזת קידוד התוכן שמשויך לאובייקט הזה.
contentLanguage מחרוזת שפת התוכן שמשויכת לאובייקט הזה.
contentType מחרוזת סוג התוכן שמשויך לאובייקט הזה.
metadata map<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(), צריך לסמן בתו בריחה (escape) את המשתנים באמצעות התחביר $(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)).data.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, ול-Cloud Firestore בלבד, פונקציות מובנות כמו get() ו-exists().
  • פונקציות יכולות לקרוא לפונקציות אחרות, אבל לא יכולות לקרוא לעצמן. העומק הכולל של מחסנית הקריאות מוגבל ל-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 מאפשר לכם לתחזק אותן בקלות רבה יותר ככל שהכללים נעשים מורכבים יותר.

השלבים הבאים

אחרי הדיון הזה בתנאים, יש לכם הבנה מתוחכמת יותר של כללים ואתם מוכנים:

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