במדריך הזה מוסבר איך לזהות נקודות חולשה נפוצות בהגדרות של Firebase Security Rules, איך לבדוק את הכללים שלכם ולשפר את האבטחה שלהם, ואיך לבדוק את השינויים לפני הפריסה שלהם.
אם קיבלתם התראה על כך שהנתונים שלכם לא מאובטחים כראוי, כדאי לבדוק את השגיאות הנפוצות האלה ולעדכן כללים שנמצאים בסיכון.
גישה ל-Firebase Security Rules
כדי להציג את Rules הקיים, משתמשים ב-CLI של Firebase או במסוף Firebase. חשוב לערוך את הכללים באותה שיטה, באופן עקבי, כדי להימנע מחתימה בטעות על העדכונים. אם אתם לא בטוחים שהכללים שהוגדרו באופן מקומי משקפים את העדכונים האחרונים, במסוף Firebase תמיד מוצגת הגרסה האחרונה של Firebase Security Rules שפרסמתם.
כדי לגשת לכללים ממסוף Firebase, בוחרים את הפרויקט ואז עוברים אל Realtime Database, Cloud Firestore או Storage. לוחצים על Rules אחרי שמגיעים למסד הנתונים או לקטגוריית האחסון הנכונים.
כדי לגשת לכללים מ-CLI של Firebase, עוברים לקובץ הכללים שמופיע בקובץ firebase.json.
הסבר על Firebase Security Rules
Firebase Security Rules להגן על הנתונים מפני משתמשים זדוניים. כשיוצרים מכונה של מסד נתונים או קטגוריה של Cloud Storage במסוף Firebase, אפשר לבחור אם לדחות את הגישה לכל המשתמשים (מצב נעול) או להעניק גישה לכל המשתמשים (מצב בדיקה). יכול להיות שתרצו להגדיר הגדרות פתוחות יותר במהלך הפיתוח, אבל חשוב להקדיש זמן להגדרה נכונה של הכללים ולהגנה על הנתונים לפני פריסת האפליקציה.
כשאתם מפתחים את האפליקציה ובודקים הגדרות שונות של הכללים, אתם יכולים להשתמש באחד מהמעבדים המקומיים של Firebase כדי להריץ את האפליקציה בסביבת פיתוח מקומית.
תרחישים נפוצים עם כללים לא מאובטחים
לפני פריסת האפליקציה, צריך לבדוק ולעדכן את Rules שהגדרתם כברירת מחדל או במהלך הפיתוח הראשוני של האפליקציה. כדי לוודא שאתם מאבטחים כראוי את נתוני המשתמשים, חשוב להימנע מהמלכודות הנפוצות הבאות.
גישה פתוחה
כשמגדירים את הפרויקט ב-Firebase, יכול להיות שהגדרתם את הכללים כך שיאפשרו גישה פתוחה במהלך הפיתוח. יכול להיות שאתם חושבים שאתם היחידים שמשתמשים באפליקציה שלכם, אבל אם פרסתם אותה, היא זמינה באינטרנט. אם לא מבצעים אימות משתמשים והגדרת כללי אבטחה, כל מי שיחליט לנחש את מזהה הפרויקט יוכל לגנוב, לשנות או למחוק את הנתונים.
לא מומלץ: גישת קריאה וכתיבה לכל המשתמשים.
// Allow read/write access to all users under any conditions // Warning: **NEVER** use this ruleset in production; it allows // anyone to overwrite your entire database. service cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow read, write: if true; } } } { // Allow read/write access to all users under any conditions // Warning: **NEVER** use this ruleset in production; it allows // anyone to overwrite your entire database. "rules": { ".read": true, ".write": true } } // Anyone can read or write to the bucket, even non-users of your app. // Because it is shared with App Engine, this will also make // files uploaded using App Engine public. // Warning: This rule makes every file in your Cloud Storage bucket accessible to any user. // Apply caution before using it in production, since it means anyone // can overwrite all your files. service firebase.storage { match /b/{bucket}/o { match /{allPaths=**} { allow read, write; } } } |
פתרון: כללים שמגבילים את הגישה לקריאה ולכתיבה.
יוצרים כללים שתואמים להיררכיית הנתונים. אחד מהפתרונות הנפוצים לבעיה הזו הוא אבטחה מבוססת-משתמשים באמצעות Firebase Authentication. מידע נוסף על אימות משתמשים באמצעות כללים בעלי התוכן בלבדservice cloud.firestore { match /databases/{database}/documents { // Allow only authenticated content owners access match /some_collection/{document} { // Allow reads and deletion if the current user owns the existing document allow read, delete: if request.auth.uid == resource.data.author_uid; // 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; } } } גישה ציבורית ופרטית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; } } } בעלי התוכן בלבד{ "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" } } } } גישה ציבורית ופרטית{ // 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" } } } בעלי התוכן בלבד// Grants a user access to a node matching their user ID service firebase.storage { match /b/{bucket}/o { // Files look like: "user/<UID>/file.txt" match /user/{userId}/{fileName} { allow read, write: if request.auth.uid == userId; } } } גישה ציבורית ופרטיתservice firebase.storage { match /b/{bucket}/o { // Files look like: "user/<UID>/file.txt" match /user/{userId}/{fileName} { allow read; allow write: if request.auth.uid == userId; } } } |
גישה לכל משתמש מאומת
לפעמים, Rules בודק אם משתמש מחובר, אבל לא מגביל את הגישה על סמך האימות הזה. אם אחד מהכללים כולל את הערך auth != null
, צריך לאשר שכל משתמש שמחובר לחשבון יקבל גישה לנתונים.
לא מומלץ: לכל משתמש שמחובר יש הרשאת קריאה וכתיבה לכל מסד הנתונים.
service cloud.firestore { match /databases/{database}/documents { match /some_collection/{document} { allow read, write: if request.auth.uid != null; } } } { "rules": { ".read": "auth.uid !== null", ".write": "auth.uid !== null" } } // Only authenticated users can read or write to the bucket service firebase.storage { match /b/{bucket}/o { match /{allPaths=**} { allow read, write: if request.auth != null; } } } |
הפתרון: צמצום הגישה באמצעות תנאי אבטחה.
כשבודקים אימות, כדאי גם להשתמש באחד ממאפייני האימות כדי להגביל עוד יותר את הגישה של משתמשים ספציפיים לקבוצות נתונים ספציפיות. מידע נוסף על מאפייני האימות השונים גישה מבוססת-תפקידservice cloud.firestore { match /databases/{database}/documents { // Assign roles to all users and refine access based on user roles 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" // Note: Checking for roles in your database using `get` (as in the code // above) or `exists` carry standard charges for read operations. } } } גישה מבוססת-מאפיינים// Give each user in your database a particular attribute // and set it to true/false // Then, use that attribute to grant access to subsets of data // For example, an "administrator" attribute set // to "true" grants write access to data service cloud.firestore { match /databases/{database}/documents { match /some_collection/{document} { allow write: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true; allow read: true; } } } גישה ציבורית ופרטית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 write: if request.auth.uid == request.resource.data.author_uid } } } בעלי התוכן בלבד{ "rules": { "some_path": { "$uid": { // Allow only authenticated content owners access to their data ".read": "auth.uid === $uid", ".write": "auth.uid === $uid" } } } } גישה מוגדרת-נתיב{ "rules": { "some_path/$uid": { ".write": "auth.uid === $uid", // Create a "public" subpath in your dataset "public": { ".read": true // or ".read": "auth.uid !== null" }, // Create a "private" subpath in your dataset "private": { ".read": "auth.uid === $uid" } } } } גישה ציבורית ופרטית{ // 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" } } } גישה מבוססת-קבוצה// Allow reads if the group ID in your token matches the file metadata `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; } בעלי התוכן בלבד// Grants a user access to a node matching their user ID service firebase.storage { match /b/{bucket}/o { // Files look like: "user/<UID>/file.txt" match /user/{userId}/{fileName} { allow read, write: if request.auth.uid == userId; } } } גישה ציבורית ופרטיתservice firebase.storage { match /b/{bucket}/o { // Files look like: "user/<UID>/file.txt" match /user/{userId}/{fileName} { allow read; allow write: if request.auth.uid == userId; } } } |
(Realtime Database) כללים שהועברו בירושה בצורה שגויה
Realtime Database Security Rules, כאשר כללים בנתיבים שטחיים יותר של הורה מבטלים כללים בצמתים עמוקים יותר של צאצאים. כשכותבים כלל בצומת צאצא, חשוב לזכור שהוא יכול להעניק רק הרשאות נוספות. אי אפשר לשפר או לבטל את הגישה לנתונים בנתיב עמוק יותר במסד הנתונים.
לא מומלץ: שינוי של כללים בנתיבים צאצאים
{ "rules": { "foo": { // allows read to /foo/* ".read": "data.child('baz').val() === true", "bar": { /* ignored, since read was allowed already */ ".read": false } } } } |
פתרון: כותבים כללים רחבים בנתיבים של ההורים ומעניקים הרשאות ספציפיות יותר בנתיבים של הצאצאים. אם נדרש רמת פירוט גבוהה יותר לגישה לנתונים, צריך לשמור על רמת פירוט גבוהה של הכללים. מידע נוסף על Realtime Database Security Rules מדורג מופיע במאמר תחביר הליבה של Realtime Database Security Rules. |
גישה סגורה
בזמן הפיתוח של האפליקציה, גישה נפוצה נוספת היא לא לאפשר גישה לנתונים. בדרך כלל, המשמעות היא שחסרתם את הגישה לקריאה ולכתיבה לכל המשתמשים, באופן הבא:
// Deny read/write access to all users under any conditions service cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow read, write: if false; } } }
{ "rules": { ".read": false, ".write": false } }
// Access to files through Cloud Storage is completely disallowed. // Files may still be accessible through App Engine or Google Cloud Storage APIs. service firebase.storage { match /b/{bucket}/o { match /{allPaths=**} { allow read, write: if false; } } }
עדיין תהיה גישה למסד הנתונים שלכם דרך Firebase Admin SDK ו-Cloud Functions. יש להשתמש בכללים האלה אם אתם מתכוונים להשתמש ב-Cloud Firestore או ב-Realtime Database כקצה עורפי לשרת בלבד בשילוב עם ה-SDK של Firebase Admin. השירות מאובטח, אבל כדאי לבדוק אם לקוחות האפליקציה יכולים לאחזר נתונים בצורה תקינה.
מידע נוסף על Cloud Firestore Security Rules ועל אופן הפעולה שלו זמין במאמר תחילת העבודה עם Cloud Firestore Security Rules.
בדיקת Cloud Firestore Security Rules
כדי לבדוק את התנהגות האפליקציה ולאמת את ההגדרות של Cloud Firestore Security Rules, תוכלו להשתמש במעבד Firebase. אפשר להשתמש במהדמטור Cloud Firestore כדי להריץ בדיקות יחידה בסביבה מקומית ולהפוך אותן לאוטומטיות, לפני שפורסים שינויים.
כדי לאמת במהירות את Firebase Security Rules במסוף Firebase, אפשר להשתמש בסימולטור הכללים של Firebase.