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

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

אבן הבניין העיקרית של Cloud Storage Security Rules היא התנאי. תנאי הוא ביטוי בוליאני שקובע אם לאשר או לדחות פעולה מסוימת. בכללים בסיסיים, אפשר להשתמש בערכים המילוליים true ו-false כתנאים. אבל השפה Firebase Security Rules for 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 <string, string> מפה כשהמשתמש מחובר לחשבון, מספק את uid, המזהה הייחודי של המשתמש, ואת token, מפה של הצהרות JWT של Firebase Authentication. אחרת, הוא יהיה null.
params <string, string> מפה מפה שמכילה את הפרמטרים של השאילתה בבקשה.
path נתיב path שמייצג את הנתיב שבו הבקשה מבוצעת.
resource <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 <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, אבל הן חייבות להסתיים בפקודת חזרה.

פונקציה מוגדרת באמצעות מילת המפתח 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 מאפשר לכם לשלוט בהם בצורה טובה יותר.

השלבים הבאים

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

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