הסבר על כללי האבטחה של מסד הנתונים בזמן אמת ב-Firebase

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

לכלל אבטחה של מסד נתונים בזמן אמת יש תחביר שדומה ל-JavaScript, והוא מגיע בארבעה סוגים:

סוגים של כללים
‎.read תיאור של הנסיבות שבהן משתמשים יכולים לקרוא את הנתונים.
.write מתאר אם ומתי הנתונים יכולים להיכתב.
.verify מגדיר את המראה של ערך בפורמט תקין, אם יש לו מאפייני צאצא וסוג הנתונים.
‎.indexOn מציין צאצא להוספה לאינדקס כדי לתמוך בסדר ובשאילתות.

Realtime Database סקירה כללית על אבטחה

ב-Firebase Realtime Database יש קבוצה מלאה של כלים לניהול האבטחה של האפליקציה. בעזרת הכלים האלה קל לאמת את המשתמשים, לאכוף את הרשאות המשתמשים ולאמת את הקלט.

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

אימות

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

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

הרשאה

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

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

הכללים .read ו-.write יורדים, כך שקבוצת הכללים הזו מעניקה גישת קריאה לכל הנתונים בנתיב /foo/ ולנתיבים עמוקים יותר כמו /foo/bar/baz. חשוב לזכור שכללי .read ו-.write שנמצאים בחלק העליון של מסד הנתונים מבטלים כללים שנמצאים בחלק התחתון, כך שגישת קריאה ל-/foo/bar/baz עדיין תתקבל בדוגמה הזו, גם אם כלל בנתיב /foo/bar/baz הוערך כ-false.

כללי האבטחה של Realtime Database כוללים משתנים מובנים ופונקציות שמאפשרות להפנות לנתיבים אחרים, לחותמות זמן בצד השרת, לפרטי אימות ועוד. זוהי דוגמה לכלל שמעניק גישת כתיבה למשתמשים מאומתים ב-/users/<uid>/. <uid> הוא מזהה המשתמש שהתקבל באמצעות Firebase Authentication.

{
  "rules": {
    "users": {
      "$uid": {
        ".write": "$uid === auth.uid"
      }
    }
  }
}

אימות נתונים

ה-Firebase Realtime Database הוא ללא סכימה. כך קל לשנות דברים במהלך הפיתוח, אבל ברגע שהאפליקציה מוכנה להפצה, חשוב שהנתונים יישארו עקביים. שפת הכללים כוללת כלל .validate שמאפשר לכם להחיל לוגיקה של אימות באמצעות אותם ביטויים שמשמשים לכללי .read ו-.write. ההבדל היחיד הוא שכללי אימות לא פועלים ברצף, כך שכל כללי האימות הרלוונטיים חייבים להניב את הערך true כדי לאפשר את הכתיבה.

הכללים האלה אוכפים את הדרישה שהנתונים שנכתבים ב-/foo/ יהיו מחרוזת של פחות מ-100 תווים:

{
  "rules": {
    "foo": {
      ".validate": "newData.isString() && newData.val().length < 100"
    }
  }
}

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

הגדרת אינדקסים של מסדי נתונים

Firebase Realtime Database מאפשר למיין נתונים ולהריץ שאילתות עליהם. במסדי נתונים עם נפח נתונים קטן, יש תמיכה בשאילתות אד-הוק, ולכן בדרך כלל אין צורך באינדקסים במהלך הפיתוח. עם זאת, לפני השקת האפליקציה חשוב לציין אינדקסים לכל השאילתות שלכם כדי להבטיח שהן ימשיכו לפעול כשהאפליקציה תתפתח.

אפשר לציין את האינדקסים באמצעות הכלל .indexOn. דוגמה להצהרת אינדקס שתוסיף לאינדקס את השדות height ו-length של רשימה של דינוזאורים:

{
  "rules": {
    "dinosaurs": {
      ".indexOn": ["height", "length"]
    }
  }
}

השלבים הבאים