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) ואז להשתמש בדייר בכללים. בדוגמאות הבאות מאפשרים לכתוב ממשתמש בדייר (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;
}
}