למד את תחביר הליבה של שפת כללי מסד הנתונים בזמן אמת

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

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

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

בניית כללי האבטחה שלך

כללי אבטחת מסד נתונים בזמן אמת מורכבים מביטויים דמויי 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 . להלן סיכום מהיר של מטרותיהם:

סוגי כללים
.לקרוא מתאר אם ומתי מותר לקרוא נתונים על ידי משתמשים.
.לִכתוֹב מתאר אם ומתי מותר לכתוב נתונים.
.לְאַמֵת מגדיר כיצד ייראה ערך בפורמט נכון, האם יש לו תכונות צאצא וסוג הנתונים.

משתני לכידת תווים כלליים

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

{
  "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/ אין השפעה כאן, מאז הגישה לא יכול לשלול נתיב ילד.

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

הערה כי .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
הערה: מוצר Firebase זו אינו זמין על יעד קליפ App.
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
}];
מָהִיר
הערה: מוצר Firebase זו איננו זמין על יעד קליפ App.
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
  });
});
מנוחה
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
הערה: מוצר Firebase זו איננו זמין על יעד קליפ App.
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
[[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
    // SUCCESS!
}];
מָהִיר
הערה: מוצר Firebase זו אינו זמין על יעד קליפ App.
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
  }
});
מנוחה
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 בזמן אמת:

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

  • לבחון את מקרי שימוש באמצעי אבטחה רגילים ואת ההגדרות הכלליות אבטחת Firebase שכתובת אותם .