כללי אבטחה בסיסיים

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

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

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

כללי ברירת המחדל: מצב נעילה

כשיוצרים מסד נתונים או מכונה לאחסון במסוף Firebase, בוחרים אם Firebase Security Rules יהיה מוגבל גישה לנתונים (מצב נעול) או יאפשר גישה לכל אחד (מצב בדיקה). ב-Cloud Firestore וב-Realtime Database, כללי ברירת המחדל של מצב נעול מונעים גישה מכל המשתמשים. ב-Cloud Storage, רק משתמשים מאומתים יכולים לגשת לקטגוריות האחסון.

Cloud Firestore

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if false;
    }
  }
}

Realtime Database

{
  "rules": {
    ".read": false,
    ".write": false
  }
}

Cloud Storage

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth != null;
    }
  }
}

כללים בסביבת הפיתוח

כשאתם עובדים על האפליקציה, יכול להיות שתרצו גישה פתוחה יחסית או ללא הגבלות לנתונים שלכם. רק חשוב לזכור לעדכן את Rules לפני פריסת האפליקציה בסביבת הייצור. חשוב גם לזכור שאם תפרסו את האפליקציה, היא תהיה נגישה לכולם – גם אם לא השקתם אותה.

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

כל המשתמשים המאומתים

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

Cloud Firestore

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if request.auth != null;
    }
  }
}

Realtime Database

{
  "rules": {
    ".read": "auth.uid !== null",
    ".write": "auth.uid !== null"
  }
}

Cloud Storage

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth != null;
    }
  }
}

כללים מוכנים לייצור

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

מומלץ לכתוב כללים בזמן שמגדירים את המבנה של הנתונים, כי האופן שבו מגדירים את הכללים משפיע על האופן שבו מגבילים את הגישה לנתונים בנתיבים שונים.

גישה של בעלי התוכן בלבד

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

מתי הכלל הזה פועל: הכלל הזה פועל היטב אם הנתונים מופרדים לפי משתמש – אם המשתמש היחיד שצריך גישה לנתונים הוא אותו משתמש שיצר את הנתונים.

כשהכלל הזה לא פועל: קבוצת הכללים הזו לא פועלת כאשר מספר משתמשים צריכים לכתוב או לקרוא את אותם נתונים – המשתמשים יחליפו את הנתונים או לא יוכלו לגשת לנתונים שהם יצרו.

כדי להגדיר את הכלל הזה: יוצרים כלל שמאשר שהמשתמש שמבקש גישה לקריאה או לכתיבה של נתונים הוא המשתמש שבבעלותו הנתונים האלה.

Cloud Firestore

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow only authenticated content owners access
    match /some_collection/{userId}/{documents=**} {
      allow read, write: if request.auth != null && request.auth.uid == userId
    }
  }
}

Realtime Database

{
  "rules": {
    "some_path": {
      "$uid": {
        // Allow only authenticated content owners access to their data
        ".read": "auth !== null && auth.uid === $uid",
        ".write": "auth !== null && auth.uid === $uid"
      }
    }
  }
}

Cloud Storage

// Grants a user access to a node matching their user ID
service firebase.storage {
  match /b/{bucket}/o {
    // Files look like: "user/<UID>/path/to/file.txt"
    match /user/{userId}/{allPaths=**} {
      allow read, write: if request.auth != null && request.auth.uid == userId;
    }
  }
}

גישה ציבורית ופרטית

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

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

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

כדי להגדיר את הכלל הזה: יוצרים כלל שמאפשר גישה לקריאה לכל המשתמשים (או לכל המשתמשים המאומתים), ומאשר שהמשתמש שכותב את הנתונים הוא הבעלים.

Cloud Firestore

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow public read access, but only content owners can write
    match /some_collection/{document} {
      // Allow public reads
      allow read: if true
      // Allow creation if the current user owns the new document
      allow create: if request.auth.uid == request.resource.data.author_uid;
      // Allow updates by the owner, and prevent change of ownership
      allow update: if request.auth.uid == request.resource.data.author_uid
                    && request.auth.uid == resource.data.author_uid;
      // Allow deletion if the current user owns the existing document
      allow delete: if request.auth.uid == resource.data.author_uid;
    }
  }
}

Realtime Database

{
// Allow anyone to read data, but only authenticated content owners can
// make changes to their data

  "rules": {
    "some_path": {
      "$uid": {
        ".read": true,
        // or ".read": "auth.uid !== null" for only authenticated users
        ".write": "auth.uid === $uid"
      }
    }
  }
}

Cloud Storage

service firebase.storage {
  match /b/{bucket}/o {
    // Files look like: "user/<UID>/path/to/file.txt"
    match /user/{userId}/{allPaths=**} {
      allow read;
      allow write: if request.auth.uid == userId;
    }
  }
}

גישה שמבוססת על מאפיינים ומבוססת על תפקיד

כדי שהכללים האלה יפעלו, צריך להגדיר מאפיינים ולהקצות אותם למשתמשים בנתונים. Firebase Security Rules בודקים את הבקשה מול הנתונים ממסד הנתונים או ממטא-נתוני הקובץ כדי לאשר או לדחות את הגישה.

כשהכלל הזה פועל: אם מקצים תפקיד למשתמשים, הכלל הזה מאפשר להגביל בקלות את הגישה לפי תפקידים או קבוצות ספציפיות של משתמשים. לדוגמה, אם אתם שומרים ציונים, תוכלו להקצות רמות גישה שונות לקבוצה 'תלמידים' (לקרוא את התוכן שלהם בלבד), לקבוצה 'מורים' (לקרוא ולכתוב בנושא שלהם) ולקבוצה 'מנהלים' (לקרוא את כל התוכן).

כשהכלל הזה לא פועל: ב-Realtime Database וב-Cloud Storage, הכללים לא יכולים להשתמש בשיטה get() שהכללים של Cloud Firestore יכולים לשלב. לכן, צריך לבנות את המבנה של מסד הנתונים או של המטא-נתונים של הקובץ כך שישקפו את המאפיינים שבהם אתם משתמשים בכללים.

כדי להגדיר את הכלל הזה: ב-Cloud Firestore, כוללים שדה במסמכים של המשתמשים שאפשר לקרוא, ואז יוצרים את הכלל כך שיקריא את השדה הזה ויקצה גישה באופן מותנה. ב-Realtime Database, יוצרים נתיב נתונים שמגדיר את המשתמשים באפליקציה ומקצה להם תפקיד בצומת צאצא.

אפשר גם להגדיר תלונות בהתאמה אישית ב-Authentication ואז לאחזר את המידע הזה מהמשתנה auth.token בכל Firebase Security Rules.

מאפיינים ותפקידים מוגדרים-נתונים

כללים אלה פועלים רק בCloud Firestore ובRealtime Database.

Cloud Firestore

חשוב לזכור: בכל פעם שהכללים כוללים קריאה, כמו הכללים שבהמשך, אתם מחויבים על פעולת קריאה ב-Cloud Firestore.

service cloud.firestore {
  match /databases/{database}/documents {
    // For attribute-based access control, Check a boolean `admin` attribute
    allow write: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true;
    allow read: true;

    // Alterntatively, for role-based access, assign specific roles to users
    match /some_collection/{document} {
     allow read: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == "Reader"
     allow write: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == "Writer"
   }
  }
}

Realtime Database

{
  "rules": {
    "some_path": {
      "${subpath}": {
        //
        ".write": "root.child('users').child(auth.uid).child('role').val() === 'admin'",
        ".read": true
      }
    }
  }
}

מאפיינים ותפקידים של הצהרות בהתאמה אישית

כדי להטמיע את הכללים האלה, מגדירים הצהרות בהתאמה אישית ב-Firebase Authentication ומשתמשים בהצהרות האלה בכללים.

Cloud Firestore

service cloud.firestore {
  match /databases/{database}/documents {
    // For attribute-based access control, check for an admin claim
    allow write: if request.auth.token.admin == true;
    allow read: true;

    // Alterntatively, for role-based access, assign specific roles to users
    match /some_collection/{document} {
     allow read: if request.auth.token.reader == "true";
     allow write: if request.auth.token.writer == "true";
   }
  }
}

Realtime Database

{
  "rules": {
    "some_path": {
      "$uid": {
        // Create a custom claim for each role or group
        // you want to leverage
        ".write": "auth.uid !== null && auth.token.writer === true",
        ".read": "auth.uid !== null && auth.token.reader === true"
      }
    }
  }
}

Cloud Storage

service firebase.storage {
  // 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;
  }
}

מאפיינים לדיירים בענן

כדי להטמיע את הכללים האלה, צריך להגדיר מגורים משותפים ב-Google Cloud Identity Platform‏ (GCIP) ואז להשתמש בדייר בכללים. בדוגמאות הבאות אפשר לכתוב הודעות ממשתמש לדייר ספציפי, למשל tenant2-m6tyz

Cloud Firestore

service cloud.firestore {
  match /databases/{database}/documents {
    // For tenant-based access control, check for a tenantID
    allow write: if request.auth.token.firebase.tenant == 'tenant2-m6tyz';
    allow read: true;
  }
}

Realtime Database

{
  "rules": {
    "some_path": {
      "$uid": {
        // Only allow reads and writes if user belongs to a specific tenant
        ".write": "auth.uid !== null && auth.token.firebase.tenant === 'tenant2-m6tyz'",
        ".read": "auth.uid !== null
      }
    }
  }
}

Cloud Storage

service firebase.storage {
  // Only allow reads and writes if user belongs to a specific tenant
  match /files/{tenantId}/{fileName} {
    allow read: if request.auth != null;
    allow write: if request.auth.token.firebase.tenant == tenantId;
  }
}