Join us for Firebase Summit on November 10, 2021. Tune in to learn how Firebase can help you accelerate app development, release with confidence, and scale with ease. Register

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

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

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

משתני לכידת Wildcard

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

{
  "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
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
});
Objective-C
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 שכתובת אותם .