تعرف على البنية الأساسية للغة قواعد أمان قاعدة البيانات في الوقت الحقيقي

تسمح لك قواعد أمان قاعدة بيانات Firebase Realtime بالتحكم في الوصول إلى البيانات المخزنة في قاعدة بياناتك. يسمح لك بناء جملة القواعد المرنة بإنشاء قواعد تتطابق مع أي شيء، بدءًا من جميع عمليات الكتابة إلى قاعدة البيانات الخاصة بك وحتى العمليات على العقد الفردية.

قواعد أمان قاعدة البيانات في الوقت الحقيقي هي تكوين تعريفي لقاعدة البيانات الخاصة بك. وهذا يعني أنه يتم تعريف القواعد بشكل منفصل عن منطق المنتج. وهذا له عدد من المزايا: العملاء ليسوا مسؤولين عن فرض الأمن، ولن تؤدي تطبيقات الأخطاء إلى تعريض بياناتك للخطر، وربما الأهم من ذلك، ليست هناك حاجة إلى مرجع وسيط، مثل الخادم، لحماية البيانات من العالم.

يصف هذا الموضوع بناء الجملة الأساسي وبنية قواعد أمان قاعدة البيانات في الوقت الفعلي المستخدمة لإنشاء مجموعات قواعد كاملة.

هيكلة قواعد الأمان الخاصة بك

تتكون قواعد أمان قاعدة البيانات في الوقت الفعلي من تعبيرات تشبه 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 . لكن النتيجة الفعلية هي خطأ:

جافا سكريبت
var db = firebase.database();
db.ref("records").once("value", function(snap) {
  // success method is not called
}, function(err) {
  // error callback triggered with PERMISSION_DENIED
});
ج موضوعية
ملاحظة: منتج Firebase هذا غير متوفر في هدف App Clip.
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 Clip.
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 ، سنحتاج إلى الوصول إليه مباشرةً:

جافا سكريبت
var db = firebase.database();
db.ref("records/rec1").once("value", function(snap) {
  // SUCCESS!
}, function(err) {
  // error callback is not called
});
ج موضوعية
ملاحظة: منتج Firebase هذا غير متوفر في هدف App Clip.
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
[[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
    // SUCCESS!
}];
سويفت
ملاحظة: منتج Firebase هذا غير متوفر في هدف App Clip.
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 Realtime:

  • تعرف على المفهوم الرئيسي التالي للغة القواعد، والشروط الديناميكية، التي تتيح لقواعدك التحقق من ترخيص المستخدم، ومقارنة البيانات الموجودة والواردة، والتحقق من صحة البيانات الواردة، والتحقق من بنية الاستعلامات الواردة من العميل، والمزيد.

  • قم بمراجعة حالات الاستخدام الأمني ​​النموذجية وتعريفات قواعد أمان Firebase التي تتناولها .