כללי האבטחה של מסדי נתונים בזמן אמת ב-Firebase מאפשרים לשלוט בגישה לנתונים שמאוחסנים במסד הנתונים. תחביר הכללים הגמיש מאפשר לך ליצור כללים שתואמים לכל דבר, החל מכל הכתיבה ועד למסד הנתונים שלכם ועד לפעולות בצמתים נפרדים.
כללי האבטחה של Realtime Database הם הגדרה הצהרתית של מסד הנתונים. כלומר, הכללים מוגדרים בנפרד מהלוגיקה של המוצר. הזה יש מספר יתרונות: הלקוחות אינם אחראים לאכוף אבטחה, באגים לא יפגעו בנתונים שלכם, והכי חשוב, אין צורך בגורם מפנה מתווך, כמו שרת, כדי להגן על הנתונים מהעולם.
בנושא הזה מתוארים כללי האבטחה הבסיסיים של מסד נתונים בזמן אמת בתחביר ובמבנה ששימשו ליצירת קבוצות כללים שלמות.
מבנה של כללי האבטחה
כללי האבטחה של מסד נתונים בזמן אמת מורכבים מביטויים דמויי JavaScript שנכללים מסמך JSON. המבנה של הכללים צריך להתאים למבנה של הנתונים שאתם שומרים במסד הנתונים.
כללים בסיסיים מזהים קבוצת צמתים שצריך לאבטח, שיטות הגישה (למשל, קריאה,
כתיבה), ותנאים שבהם תינתן גישה או תידחה.
בדוגמאות הבאות, התנאים שלנו יהיו הצהרות פשוטות של true
ו-false
, אבל בנושא הבא נסביר על דרכים דינמיות יותר להבעת תנאים.
לדוגמה, אם אנחנו מנסים לאבטח child_node
במסגרת parent_node
,
התחביר הכללי שצריך לעקוב אחריו הוא:
{ "rules": { "parent_node": { "child_node": { ".read": <condition>, ".write": <condition>, ".validate": <condition>, } } } }
בואו ניישם את התבנית הזו. לדוגמה, נניח שאתם עוקבים אחרי רשימה של הודעות ויש לכם נתונים שנראים כך:
{ "messages": { "message0": { "content": "Hello", "timestamp": 1405704370369 }, "message1": { "content": "Goodbye", "timestamp": 1405704395231 }, ... } }
הכללים צריכים להיות מובנים באופן דומה. הנה קבוצת כללים לאבטחה לקריאה בלבד שעשויה להתאים למבנה הנתונים הזה. הזה שממחישה איך אנחנו מציינים צמתים של מסדי נתונים שעליהם חלים הכללים תנאים להערכת הכללים בצמתים האלה.
{ "rules": { // For requests to access the 'messages' node... "messages": { // ...and the individual wildcarded 'message' nodes beneath // (we'll cover wildcarding variables more a bit later).... "$message": { // For each message, allow a read operation if <condition>. In this // case, we specify our condition as "true", so read access is always granted. ".read": "true", // For read-only behavior, we specify that for write operations, our // condition is false. ".write": "false" } } } }
פעולות בסיסיות של כללים
יש שלושה סוגים של כללים לאכיפת האבטחה לפי סוג
פעולה שמבוצעת על הנתונים: .write
, .read
ו-.validate
. כאן
הוא סיכום קצר של המטרות שלהם:
סוגים של כללים | |
---|---|
.read | תיאור של הנסיבות שבהן המשתמשים יכולים לקרוא את הנתונים. |
.write | מתאר אם ומתי מותר לכתוב נתונים. |
.validate | מגדיר את המראה של ערך בפורמט תקין, אם יש לו מאפייני צאצא וסוג הנתונים. |
משתני צילום עם תווים כלליים לחיפוש
כל ההצהרות של הכללים מפנות לצמתים. הצהרה יכולה להצביע על
או להשתמש במשתני תיעוד של התו הכללי לחיפוש $
כדי להצביע על קבוצות צמתים
בהיררכיה. משתמשים במשתני האחזור האלה כדי לאחסן את הערך של מפתחות הצמתים לשימוש בהצהרות של כללים הבאים. השיטה הזו מאפשרת לכתוב
Rules תנאים מורכבים יותר, נעסוק בהם בפירוט
בנושא הבא.
{ "rules": { "rooms": { // this rule applies to any child of /rooms/, the key for each room id // is stored inside $room_id variable for reference "$room_id": { "topic": { // the room's topic can be changed if the room id has "public" in it ".write": "$room_id.contains('public')" } } } } }
אפשר להשתמש גם במשתנים הדינמיים של $
במקביל לנתיב קבוע
שמות. בדוגמה הזו אנחנו משתמשים במשתנה $other
כדי להצהיר על כלל .validate
שמבטיח של-widget
אין צאצאים מלבד title
ו-color
.
כל פעולת כתיבה שתגרום ליצירה של צאצאים נוספים תיכשל.
{ "rules": { "widget": { // a widget can have a title or color attribute "title": { ".validate": true }, "color": { ".validate": true }, // but no other child paths are allowed // in this case, $other means any key excluding "title" and "color" "$other": { ".validate": false } } } }
דירוג כללי קריאה וכתיבה
הכללים .read
ו-.write
פועלים מלמעלה למטה, כאשר כללים שטחיים יותר מבטלים כללים עמוקים יותר. אם כלל מעניק הרשאות קריאה או כתיבה ברמה מסוימת
ואז הוא גם מעניק גישה
כל צומתי הצאצא שתחתיו. חשוב על המבנה הבא:
{ "rules": { "foo": { // allows read to /foo/* ".read": "data.child('baz').val() === true", "bar": { /* ignored, since read was allowed already */ ".read": false } } } }
מבנה האבטחה הזה מאפשר לקרוא מ-/bar/
בכל פעם ש-/foo/
מכיל צאצא baz
עם הערך true
.
לכלל ".read": false
בקטע /foo/bar/
אין השפעה כאן, כי אי אפשר לבטל את הגישה דרך נתיב צאצא.
יכול להיות שהשימוש ב-OR לא נראה אינטואיטיבי באופן מיידי, אבל זוהי תכונה חזקה בשפת הכללים שמאפשרת להטמיע הרשאות גישה מורכבות מאוד במאמץ מינימלי. נרחיב על כך בהמשך המדריך, כשנדבר על אבטחה מבוססת-משתמשים.
חשוב לשים לב שהכללים של .validate
לא יורדים. כל כללי האימות
חייב להיות תואם בכל הרמות בהיררכיה כדי שפעולת הכתיבה תאושר.
כללים אינם מסננים
הכללים חלים באופן אטומי. כלומר, קריאה או כתיבה הפעולה נכשלת באופן מיידי אם אין כלל במיקום הזה או במיקום מיקום הורה שמעניק גישה. גם אם יש גישה לכל נתיב הצאצא שמושפע, הקריאה במיקום ההורה תיכשל לחלוטין. חשוב על המבנה הזה:
{ "rules": { "records": { "rec1": { ".read": true }, "rec2": { ".read": false } } } }
אם לא מבינים שהכללים נבדקים באופן אטומי, יכול להיות שייראה לכם שהאחזור של הנתיב /records/
יחזיר את הערך rec1
אבל לא את הערך rec2
. עם זאת, התוצאה בפועל היא שגיאה:
JavaScript
var db = firebase.database(); db.ref("records").once("value", function(snap) { // success method is not called }, function(err) { // error callback triggered with PERMISSION_DENIED });
Objective-C
FIRDatabaseReference *ref = [[FIRDatabase database] reference]; [[_ref child:@"records"] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) { // success block is not called } withCancelBlock:^(NSError * _Nonnull error) { // cancel block triggered with PERMISSION_DENIED }];
Swift
var ref = FIRDatabase.database().reference() ref.child("records").observeSingleEventOfType(.Value, withBlock: { snapshot in // success block is not called }, withCancelBlock: { error in // cancel block triggered with PERMISSION_DENIED })
Java
FirebaseDatabase database = FirebaseDatabase.getInstance(); DatabaseReference ref = database.getReference("records"); ref.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot snapshot) { // success method is not called } @Override public void onCancelled(FirebaseError firebaseError) { // error callback triggered with PERMISSION_DENIED }); });
REST
curl https://docs-examples.firebaseio.com/rest/records/ # response returns a PERMISSION_DENIED error
מכיוון שפעולת הקריאה ב/records/
היא אטומית, ואין
קריאה של כלל שמעניק גישה לכל הנתונים במסגרת /records/
,
הפעולה הזו תקפיץ הודעת שגיאה PERMISSION_DENIED
. אם נבדוק את הכלל הזה בסימולטור האבטחה במסוף Firebase, נראה שפעולת הקריאה נדחתה כי לא היה כלל קריאה שהתיר גישה לנתיב /records/
. עם זאת, חשוב לשים לב שהכלל של rec1
מעולם לא בוצעה הערכה כי היא לא הייתה בנתיב שביקשנו. כדי לאחזר את rec1
, נצטרך לגשת אליו ישירות:
JavaScript
var db = firebase.database(); db.ref("records/rec1").once("value", function(snap) { // SUCCESS! }, function(err) { // error callback is not called });
Objective-C
FIRDatabaseReference *ref = [[FIRDatabase database] reference]; [[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) { // SUCCESS! }];
Swift
var ref = FIRDatabase.database().reference() ref.child("records/rec1").observeSingleEventOfType(.Value, withBlock: { snapshot in // SUCCESS! })
Java
FirebaseDatabase database = FirebaseDatabase.getInstance(); DatabaseReference ref = database.getReference("records/rec1"); ref.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot snapshot) { // SUCCESS! } @Override public void onCancelled(FirebaseError firebaseError) { // error callback is not called } });
REST
curl https://docs-examples.firebaseio.com/rest/records/rec1 # SUCCESS!
הצהרות חופפות
ייתכן מצב שבו יותר מכלל אחד יחול על צומת. ב
שבו מספר ביטויים של כללים מזהים צומת, שיטת הגישה
נדחתה אם אחד או יותר מהתנאים הם false
:
{ "rules": { "messages": { // A rule expression that applies to all nodes in the 'messages' node "$message": { ".read": "true", ".write": "true" }, // A second rule expression applying specifically to the 'message1` node "message1": { ".read": "false", ".write": "false" } } } }
בדוגמה שלמעלה, פעולות קריאה לצומת message1
יידחו כי הערך של הכלל השני הוא תמיד false
, למרות שהערך של הכלל הראשון הוא תמיד true
.
השלבים הבאים
אתם יכולים להעמיק את ההבנה שלכם לגבי כללי האבטחה של מסד הנתונים בזמן אמת ב-Firebase:
נלמד את המושג החשוב הבא בשפת Rules, תנאים דינמיים, שמאפשרים ל-Rules לבדוק את הרשאת המשתמש, להשוות בין נתונים קיימים לנתונים נכנסים, לאמת נתונים נכנסים, לבדוק את המבנה של שאילתות שמגיעות מהלקוח ועוד.
כדאי לעיין בתרחישים לדוגמה בתחום האבטחה ובהגדרות של כללי האבטחה של Firebase שמיועדות לטפל בהם.