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

כללי האבטחה של מסד נתונים בזמן אמת של 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
});
מטרה-ג
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
}];
מָהִיר
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
})
ג'אווה
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
});
מטרה-ג
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
[[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
    // SUCCESS!
}];
מָהִיר
var ref = FIRDatabase.database().reference()
ref.child("records/rec1").observeSingleEventOfType(.Value, withBlock: { snapshot in
    // SUCCESS!
})
ג'אווה
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 שכתובת אותם .