حماية البيانات في Firebase Database

Firebase Database Security Rules

AbdulAlim Rajjoubمنذ سنة

ماهي Firebase Security Rules ؟

هو عبارة عن ملف JSON يحتوي على إعدادات الكتابة والقراءة في قاعدة البيانات Firebase Database ,أي من الذي يستطيع كتابة وقراءة أي بيانات في قاعدة البيانات.

عندما تتم أي عملية قراءة أو كتابة على قاعدة البيانات فإنه يتم الرجوع الى هذا الملف والتحقق من الصلاحيات المطلوبة ,وعند مطابقة الصلاحيات ستتم عملية الكتابة أو القراءة.

عند رفض عملية القراءة او الكتابة فإن العملية عند Client في الأندرويد على سبيل المثال ستعود ب Failed, Permissions Denied .

ملاحظة:هذه الإعدادات سيتم تجاهلها عند استخدام Firebase Admin SDK ,عند استخدام Cloud Functions مثلاً 

 

نتوجه الى Firebase Console لنرى شكل هذه الإعدادات

إفتراضياً ستكون الإعدادات بهذا الشكل.

بهذا الشكل فقط المستخدمين الذين قامو بتسجيل الدخول باستخدام Firebase Auth يمكنهم الكتابة والقراءة

{
  "rules": {
    ".read": "auth != null",
    ".write": "auth != null"
  }
}

ولنتأكد من الموضوع يمكننا استخدام ميزة Simulator الموجودة في Firebase Console ,وهو عبارة عن محاكي يمكننا تجربة عملية قراءة أو كتابة وهمية لنرى هل تمت أم لا.

 

بهذا الشكل قمنا بتجربة عملية قراءة على الRef الرئيسي /  ,وكما نرى Authenticated لم نقم بتفعيلها أي أن المستخدم لم يقم بتسجيل الدخول

ونرى على اليسار السطر الذي قام بالتحكم بالعملية.

ونفس الأمر ينطبق بالنسبة لصلاحيات الكتابة

 

الآن لنجرب أن نقوم بتشغيل Authenticated  ,أي أن المستخدم قام بتسجيل الدخول

وعند تشغيل هذا الخيار سنرى أنه يمكننا اختيار نوع المستخدم ,هل هو Anonymous أم Facebook او Google الخ.. مع وجود UID

فعلى سبيل المثال يمكننا فقط جعل المستخدمين الذين قامو بتسجيل الدخول عبر Google أن يقومو بعمل قراءة او كتابة من قاعدة البيانات كما سنشاهد لاحقاً

عموماً نقوم باختيار أي نوع ونعمل Run 

وسنرى أنه تمت عملية القراءة بنجاح مع وجود السطر الذي قام بإعطاء الصلاحية

 

إذا أردنا إعطاء الصلاحيات لأي شخص على هذا الكوكب :D  بدون تسجيل دخول فيمكنك ذلك عبر 

 

{
  "rules": {
    ".read": true,
    ".write": true
  }
}

ملاحظة:بهذا الشكل يمكن أي شخص أن يقوم بحذف كافة البيانات لديك عبر أمر Curl واحد!

 

والعكس اذا أردت منع أي شخص من استخدام هذه البيانات 

بهذا الشكل لن يستطيع أي شخص وحتى الذين قامو بتسجيل الدخول من عرض او الكتابة على قاعدة البيانات

{
  "rules": {
    ".read": false,
    ".write": false
  }
}

 

تمكين القراءة او الكتابة لنوع معين

سنقوم بجعل فقط المستخدمين الذين قامو بتسجيل الدخول عبر Google على سبيل المثال من عرض او كتابة البيانات

قمنا بالتأكد من المستخدم قام بتسجيل الدخول وأيضاً auth.provider هو Google

{
  "rules": {
    ".read": "auth != null && auth.provider === 'google'",
    ".write": "auth != null && auth.provider === 'google'"
  }
}

نفس الأمر ينطبق على Facebook 

{
  "rules": {
    ".read": "auth != null && auth.provider === 'facebook'",
    ".write": "auth != null && auth.provider === 'facebook'"
  }
}

Facebook و Google مع بعضهم البعض

{
  "rules": {
    ".read": "auth != null && auth.provider === 'google' ||
    auth.provider === 'facebook'",
    ".write": "auth != null && auth.provider === 'google' ||
    auth.provider === 'facebook'"
  }
}

 

التحكم فقط من خلال المدير او Admin

لنفترض أن لدينا تطبيق يحتوي على بعض المطاعم ومعلوماتها ويمكن للمستخدم رؤية المطعم ومكانه وتقييمه والخ..

ونريد أن يكون فقط للمدير أو Admin الصلاحية لحذف أو تعديل بيانات المطعم, وللمستخدم العادي الصلاحية فقط في عرض بيانات المطعم

سيكون لدينا في قاعدة البيانات حقل Users سيكون بهم كافة المستخدمين من ضمنهم المدير او Admin

بهذا الشكل 

users
      u283210das0sada
              name: "User1"
      
        u32s929s02ads92
               name: "User2"

الآن سنقوم بجعل المستخدم الأول Admin

وذلك عبر اضافة حقل isAdmin وجعل قيمته true

users
      u283210das0sada
              isAdmin : true
              name: "User1"

      
        u32s929s02ads92
               name: "User2"

الآن نعود الى Firebase Security Rules لنضيف هذا الشرط

بالنسبة لإعدادات القراءة فقمنا بتفعيلها للمستخدمين الذين قامو بتسجيل الدخول

أما بالنسبة للكتابة فقمنا بالتأكد أولا من أن المستخدم قد قام بتسجيل الدخول وأيضاً قمنا بالتحقق من أن الحقل 'users' يحتوي على uid وداخل هذا uid يجب أن يحتوي على الحقل isAdmin وقيمته true

الآن اذا قمنا بالتجربة على أي user ليس لديه صلاحية isAdmin : true فإنه لايمكنه تعديل أو كتابة أي شيئ

{
  "rules": {
   ".read":"auth != null",
     ".write":"auth != null && root.child('users')
       .child(auth.uid).child('isAdmin').val() == true"
    
  }
}

 

كل ماسبق كنا نقوم بجعل القواعد عامة, أي أنها تنطبق على كافة Nodes (الحقول) في الداتابيز ,ولكن ماذا اذا أردنا بعض التخصيص ,بمعنى أنه نريد تخصيص قواعد معينة لكل حقل في الداتابيز.

لفهم الموضوع أكثر سنفترض أنه لدينا تطبيق دردشة ونريد اذا كان المستخدم الأول قد قام بحظر المستخدم الثاني,عندها سنقوم بمنع المستخدم الثاني من رؤية صورة البروفايل الخاصة بالمستخدم الأول

لنفترض أنه لدينا نفس الStrcuture  السابق لقاعدة البيانات ولكن بإضافة رابط الصورة الخاصة بكل مستخدم

users
      u283210das0sada
              name: "User1"
              photo:"https://......"
      
        u32s929s02ads92
               name: "User2"
               photo:"https://......"

الآن سنقوم بإنشاء حقل خاص blockedUsers يحتوي على المستخدمين الذين قامو بعمل حظر لمستخدمين آخرين

بهذا الشكل على سبيل المثال

blockedUsers
      uid1
           uid2:true
           uid3:true
           ..............
           ..............

عندما يقوم المستخدم الأول  User1 بحظر المستخدم الثاني User2 فسيصبح بهذا الشكل

users
      u283210das0sada
              name: "User1"
              photo:"https://......"
      
        u32s929s02ads92
               name: "User2"
               photo:"https://......"




blockedUsers
      u283210das0sada
           u32s929s02ads92 : true

نعود الآن الى Secuirty Rules 

وداخل القوسين rules نضع القواعد الخاصة بالحقل "users' وداخله قمنا بتعريف بما يسمى ب 'Wildcard' ($) ,اذا كنت قد استخدمت Cloud Functions سابقاً فإنها تعتمد على نفس المبدأ

والفكرة هي الحصول القيمة داخل هذا الحقل ففي حالتنا هذه قمنا بأخذ uid الذي يحاول المستخدم الحصول على هذه المعلومات (المستخدم الثاني User2يحاول الحصول على بيانات المستخدم الثاني)

 ويمكنك تسميتها بأي اسم بدل من UID ولكن يجب عليك وضع اشارة قبلها

ثم قمنا بوضع القواعد فقط على حقل photo ووضعنا قواعد الكتابة(سنغيرها لاحقاً)

وبالنسبة للقراءة فإنه يجب أن يكون المستخدم قد قام بتسجيل الدخول أن يكون الحقل 'blockedUsers/$uid' لا يحتوي على uid الشخص الثاني

{
  "rules": {
   "users":{
     "$uid":{
       "photo":{
         ".write":"auth != null",
           ".read": "auth != null && !root.child('blockedUsers')
             .child($uid).child(auth.uid).exists()"
       }
     }
   }
  }
}

سنجرب على Simulator ونضع uid الشخص المحظور

وعند وضع uid اخر على سبيل المثال تتم عملية القراءة بنجاح

 

اذا قمنا بتجريب نفس الطريقة على الحقل name فإنه سيتم رفض العملية وذلك لأننا لم نقم بتحديد أية قواعد لهذا الحقل,كنت أتمنى لو أنه بإمكاننا تحديد فقط القواعد للحقول التي نريدها ونستطيع وضع قاعدة عامة عند عدم تواجد قاعدة خاصة لهذا الحقل.

لهذا سنقوم بتحديد قاعدة للحقل name ونريد أن يستطيع أي شخص وحتى ان كان محظوراً من رؤية بيانات الشخص الآخر

{
  "rules": {
   "users":{
     "$uid":{
       "photo":{
         ".write":"auth != null,
           ".read": "auth != null && !root.child('blockedUsers')
             .child($uid).child(auth.uid).exists()"
       },
         "name":{
           ".write":"auth != null",
             ".read": "auth != null"
           
         }
     }
   }
  }
}

الآن نريد تأمين بيانات المستخدم بشكل أكبر عن طريق جعل المستخدم نفسه فقط من أن يستطيع تغيير بياناته

وسنستخدم نفس الطريقة عبر Wildcard

قمنا بالتحقق من أن auth.uid وهو uid الشخص الذي ينفذ الطلب من أنه يساوي $uid  

{
  "rules": {
   "users":{
     "$uid":{
       "photo":{
         ".write":"auth != null && auth.uid === $uid",
           ".read": "auth != null && !root.child('blockedUsers')
             .child($uid).child(auth.uid).exists()"
       },
         "name":{
           ".write":"auth != null && auth.uid === $uid",
             ".read": "auth != null"
           
         }
     }
   }
  }
}

اذا قمنا بمحاولة اضافة شخص ما الى blockedUsers فسيظهر لنا خطأ  :/ وذلك لأنه لم تتم تحديد قاعدة لهذا الحقل 

وسنجعل فقط المستخدم نفسه من عرض وكتابة البيانات

{
  "rules": {
   "users":{
     "$uid":{
       "photo":{
         ".write":"auth != null && auth.uid === $uid",
           ".read": "auth != null && !root.child('blockedUsers')
             .child($uid).child(auth.uid).exists()"
       },
         "name":{
           ".write":"auth != null && auth.uid === $uid",
             ".read": "auth != null"
           
         }
     }
   },
     "blockedUsers":{
       "$uid":{
         ".read":"auth != null && auth.uid === $uid",
           ".write":"auth != null && auth.uid === $uid"
       }
     }
  }
}

 

بعض المصادر التي قد تهمك

1 , 2

كلمات دليلية: database firebase security
3
إعجاب
1681
مشاهدات
2
مشاركة
2
متابع
متميز
محتوى رهيب

التعليقات (1)

kaaed japeli:

Sehr Gut

لايوجد لديك حساب في عالم البرمجة؟

تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !