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

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 read: if true
      allow create: if request.auth.uid == request.resource.data.author_uid;
      allow update, 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;
  }
}

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

כדי להטמיע את הכללים האלה, צריך להגדיר multitenancy ב-Google Cloud Identity Platform (GCIP) ואז מינוף של הדייר בכללים שלכם. הדוגמאות הבאות מאפשרות כתיבה מדייר (tenant) ספציפי, למשל: 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;
  }
}