استخدام الشروط في قواعد أمان Firebase Cloud Storage

يستند هذا الدليل إلى دليل تعلُّم البنية الأساسية للغة Firebase Security Rules لعرض كيفية إضافة شروط إلى Firebase Security Rules في Cloud Storage.

الوحدة الأساسية في Cloud Storage Security Rules هي الشرط. الشرط هو تعبير منطقي يحدّد ما إذا كان يجب السماح بعملية معيّنة أو رفضها. بالنسبة إلى القواعد الأساسية، يعمل استخدام القيم الحرفية true وfalse كشروط بشكل جيد جدًا. لكنّ Firebase Security Rules لـ Cloud Storage تمنحك طرقًا لكتابة شروط أكثر تعقيدًا يمكنها:

  • التحقّق من مصادقة المستخدم
  • التحقّق من صحة البيانات الواردة

المصادقة

Firebase Security Rules لـ Cloud Storage تتكامل مع Firebase Authentication لتوفير مصادقة قوية للمستخدمين في Cloud Storage. ويتيح ذلك التحكّم الدقيق في الوصول استنادًا إلى مطالبات رمز Firebase Authentication المميّز.

عندما ينفّذ مستخدم تمّت مصادقته طلبًا في Cloud Storage، يتم ملء المتغيّر الـ request.auth برقم تعريف المستخدم uid (request.auth.uid) بالإضافة إلى مطالبات رمز JWT المميّز لـ Firebase Authentication (request.auth.token).

بالإضافة إلى ذلك، عند استخدام المصادقة المخصّصة، تظهر مطالبات إضافية في الحقل request.auth.token.

عندما ينفّذ مستخدم لم تتم مصادقته طلبًا، يكون المتغيّر request.auth هو null.

باستخدام هذه البيانات، هناك عدة طرق شائعة لاستخدام المصادقة لتأمين الملفات:

  • علني: تجاهُل request.auth
  • خاص بالمستخدمين الذين تمّت مصادقتهم: التحقّق من أنّ request.auth ليس null
  • خاص بالمستخدم: التحقّق من أنّ request.auth.uid يساوي uid المسار
  • خاص بالمجموعة: التحقّق من مطالبات الرمز المميّز المخصّص لمطابقة مطالبة معيّنة، أو قراءة البيانات الوصفية للملف لمعرفة ما إذا كان حقل البيانات الوصفية متوفرًا

عام

يمكن اعتبار أي قاعدة لا تأخذ سياق request.auth في الاعتبار قاعدة public، لأنّها لا تأخذ سياق مصادقة المستخدم في الاعتبار. يمكن أن تكون هذه القواعد مفيدة لعرض البيانات العامة، مثل مواد عرض الألعاب أو الملفات الصوتية أو المحتوى الثابت الآخر.

// Anyone to read a public image if the file is less than 100kB
// Anyone can upload a public file ending in '.txt'
match /public/{imageId} {
  allow read: if resource.size < 100 * 1024;
  allow write: if imageId.matches(".*\\.txt");
}

خاص بالمستخدمين الذين تمّت مصادقتهم

في حالات معيّنة، قد تريد أن يتمكّن جميع المستخدمين الذين تمّت مصادقتهم في تطبيقك من عرض البيانات، ولكن ليس المستخدمون الذين لم تتم مصادقتهم. بما أنّ المتغيّر request.auth هو null لجميع المستخدمين الذين لم تتم مصادقتهم، كل ما عليك فعله هو التحقّق من وجود المتغيّر request.auth لطلب المصادقة:

// Require authentication on all internal image reads
match /internal/{imageId} {
  allow read: if request.auth != null;
}

خاص بالمستخدم

إنّ حالة الاستخدام الأكثر شيوعًا للمتغيّر request.auth هي منح المستخدمين الفرديين أذونات دقيقة على ملفاتهم، بدءًا من تحميل صور الملف الشخصي إلى قراءة المستندات الخاصة.

بما أنّ الملفات في Cloud Storage لها "مسار" كامل إلى الملف، كل ما يلزم لجعل الملف خاضعًا لتحكّم المستخدم هو جزء من المعلومات الفريدة التي تحدّد هوية المستخدم في بادئة اسم الملف (مثل uid المستخدم) التي يمكن التحقّق منها عند تقييم القاعدة:

// Only a user can upload their profile picture, but anyone can view it
match /users/{userId}/profilePicture.png {
  allow read;
  allow write: if request.auth.uid == userId;
}

خاص بالمجموعة

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

  • إنشاء رمز مميّز مخصّص لـ Firebase Authentication Firebase Authentication يحتوي على معلومات إضافية عن أحد أعضاء المجموعة (مثل رقم تعريف المجموعة)
  • تضمين معلومات المجموعة (مثل رقم تعريف المجموعة أو قائمة بـ uids المصرّح بها) في البيانات الوصفية للملف

بعد تخزين هذه البيانات في الرمز المميّز أو البيانات الوصفية للملف، يمكن الرجوع إليها من داخل القاعدة:

// Allow reads if the group ID in your token matches the file metadata's `owner` property
// Allow writes if the group ID is in the user's custom token
match /files/{groupId}/{fileName} {
  allow read: if resource.metadata.owner == request.auth.token.groupId;
  allow write: if request.auth.token.groupId == groupId;
}

تقييم الطلب

يتم تقييم عمليات التحميل والتنزيل وتغييرات البيانات الوصفية والحذف باستخدام الـ request الذي يتم إرساله إلى Cloud Storage. بالإضافة إلى رقم تعريف المستخدم الفريد و حِمل Firebase Authentication في الكائن request.auth كما هو موضّح أعلاه، يحتوي المتغيّر request على مسار الملف الذي يتم تنفيذ الطلب فيه، والوقت الذي يتم فيه استلام الطلب، وقيمة resource الجديدة إذا كان الطلب هو طلب كتابة.

يحتوي الكائن request أيضًا على رقم تعريف المستخدم الفريد وحِمل Firebase Authentication في الكائن request.auth، وسيتم شرح ذلك بالتفصيل في قسم الأمان المستند إلى المستخدم في المستندات.

في ما يلي قائمة كاملة بالخصائص في الكائن request:

الموقع النوع الوصف
auth map<string, string> عند تسجيل دخول المستخدم، يوفّر هذا الحقل uid، وهو رقم تعريف المستخدم الفريد، و token، وهو خريطة لمطالبات رمز Firebase Authentication المميّز. وفي ما عدا ذلك، سيكون null.
params map<string, string> خريطة تحتوي على مَعلمات طلب البحث في الطلب.
path path `مسار` يمثّل المسار الذي يتم تنفيذ الطلب فيه.path
resource map<string, string> قيمة المورد الجديدة، لا تظهر إلا في طلبات write
time timestamp طابع زمني يمثّل وقت الخادم الذي يتم فيه تقييم الطلب.

تقييم المورد

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

Firebase Security Rules لـ Cloud Storage توفّر البيانات الوصفية للملف في الكائن resource ، الذي يحتوي على أزواج المفاتيح والقيم للبيانات الوصفية التي تظهر في كائن Cloud Storage. يمكن فحص هذه الخصائص في read أو write لضمان سلامة البيانات.

في طلبات write (مثل عمليات التحميل وتعديلات البيانات الوصفية والحذف)، بالإضافة إلى الكائن resource الذي يحتوي على البيانات الوصفية للملف الذي يتوفّر حاليًا في مسار الطلب، يمكنك أيضًا استخدام الكائن request.resource الذي يحتوي على مجموعة فرعية من البيانات الوصفية للملف التي سيتم كتابتها إذا تم السماح بالكتابة. يمكنك استخدام هاتين القيمتَين لضمان سلامة البيانات أو فرض قيود التطبيق، مثل نوع الملف أو حجمه.

في ما يلي قائمة كاملة بالخصائص في الكائن resource:

الموقع النوع الوصف
name string الاسم الكامل للكائن
bucket string اسم المستودع الذي يقيم فيه هذا الكائن.
generation int جيل كائن Google Cloud Storage لهذا الكائن.
metageneration int جيل البيانات الوصفية لكائن Google Cloud Storage لهذا الكائن.
size int حجم الكائن بالبايت.
timeCreated timestamp طابع زمني يمثّل وقت إنشاء الكائن.
updated timestamp طابع زمني يمثّل آخر وقت تم فيه تعديل الكائن.
md5Hash string تجزئة MD5 للكائن.
crc32c string تجزئة crc32c للكائن.
etag string علامة etag المرتبطة بهذا الكائن.
contentDisposition string طريقة عرض المحتوى المرتبطة بهذا الكائن.
contentEncoding string ترميز المحتوى المرتبط بهذا الكائن.
contentLanguage string لغة المحتوى المرتبطة بهذا الكائن.
contentType string نوع المحتوى المرتبط بهذا الكائن.
metadata map<string, string> أزواج المفاتيح والقيم للبيانات الوصفية المخصّصة الإضافية التي يحدّدها المطوّر.

request.resource يحتوي على كل هذه الخصائص باستثناء generation و metageneration وetag وtimeCreated وupdated.

التحسين باستخدام Cloud Firestore

يمكنك الوصول إلى المستندات في Cloud Firestore لتقييم معايير التفويض الأخرى.

باستخدام الدالتَين firestore.get() وfirestore.exists()، يمكن لقواعد الأمان تقييم الطلبات الواردة مقابل المستندات في Cloud Firestore. تتوقّع الدالتان firestore.get() وfirestore.exists() مسارات مستندات محدّدة بالكامل. عند استخدام المتغيّرات لإنشاء مسارات للدالتَين firestore.get() وfirestore.exists()، عليك إلغاء المتغيّرات بشكل صريح باستخدام البنية $(variable).

في المثال أدناه، نرى قاعدة تقصر إذن القراءة للملفات على المستخدمين الذين هم أعضاء في نوادٍ معيّنة.

service firebase.storage {
  match /b/{bucket}/o {
    match /users/{club}/files/{fileId} {
      allow read: if club in
        firestore.get(/databases/(default)/documents/users/$(request.auth.id)).data.memberships
    }
  }
}
في المثال التالي، لا يمكن أن يرى صور المستخدم إلا أصدقاؤه.
service firebase.storage {
  match /b/{bucket}/o {
    match /users/{userId}/photos/{fileId} {
      allow read: if
        firestore.exists(/databases/(default)/documents/users/$(userId)/friends/$(request.auth.id))
    }
  }
}

بعد إنشاء Cloud Storage Security Rules الأولى التي تستخدم دالات Cloud Firestore هذه وحفظها، سيُطلب منك في Firebase console أو Firebase CLI تفعيل الأذونات لربط المنتجَين.

يمكنك إيقاف الميزة عن طريق إزالة دور IAM، كما هو موضّح في إدارة Firebase Security Rules ونشرها.

التحقّق من صحة البيانات

Firebase Security Rules لـ Cloud Storage يمكن أيضًا استخدامها للتحقّق من صحة البيانات، بما في ذلك التحقّق من صحة اسم الملف ومساره بالإضافة إلى خصائص البيانات الوصفية للملف، مثل contentType و size.

service firebase.storage {
  match /b/{bucket}/o {
    match /images/{imageId} {
      // Only allow uploads of any image file that's less than 5MB
      allow write: if request.resource.size < 5 * 1024 * 1024
                   && request.resource.contentType.matches('image/.*');
    }
  }
}

الدوال المخصّصة

عندما تصبح Firebase Security Rules أكثر تعقيدًا، قد تحتاج إلى تضمين مجموعات من الشروط في دوال يمكنك إعادة استخدامها في مجموعة القواعد. تتوافق قواعد الأمان مع الدوال المخصّصة. تشبه بنية الدوال المخصّصة JavaScript إلى حد ما، ولكنّ Firebase Security Rules دوال مكتوبة بلغة خاصة بالمجال تتضمّن بعض القيود المهمة:

  • لا يمكن أن تحتوي الدوال إلا على عبارة return واحدة. ولا يمكن أن تحتوي على أي منطق إضافي. على سبيل المثال، لا يمكنها تنفيذ حلقات أو استدعاء خدمات خارجية.
  • يمكن للدوال الوصول تلقائيًا إلى الدوال والمتغيّرات من النطاق الذي تم تعريفها فيه. على سبيل المثال، يمكن لدالة تم تعريفها ضمن نطاق service firebase.storage الوصول إلى resource المتغيّر، وبالنسبة إلى Cloud Firestore فقط، يمكنها الوصول إلى الدوال المضمّنة مثل get() وexists().
  • يمكن للدوال استدعاء دوال أخرى، ولكن لا يمكنها استدعاء نفسها. يقتصر إجمالي عمق مكدس الاستدعاء على 10.
  • في الإصدار rules2، يمكن للدوال تحديد المتغيّرات باستخدام الكلمة الرئيسية let. يمكن أن تحتوي الدوال على أي عدد من عمليات الربط باستخدام `let`، ولكن يجب أن تنتهي بعبارة `return`.

يتم تعريف الدالة باستخدام الكلمة الرئيسية function وتأخذ صفرًا أو أكثر من الوسيطات. على سبيل المثال، قد تحتاج إلى دمج نوعَي الشروط المستخدَمة في الأمثلة أعلاه في دالة واحدة:

service firebase.storage {
  match /b/{bucket}/o {
    // True if the user is signed in or the requested data is 'public'
    function signedInOrPublic() {
      return request.auth.uid != null || resource.data.visibility == 'public';
    }
    match /images/{imageId} {
      allow read, write: if signedInOrPublic();
    }
    match /mp3s/{mp3Ids} {
      allow read: if signedInOrPublic();
    }
  }
}

يؤدي استخدام الدوال في Firebase Security Rules إلى تسهيل صيانتها مع ازدياد التعقيد في القواعد.

الخطوات التالية

بعد هذه المناقشة حول الشروط، أصبح لديك فهم أكثر تطورًا للقواعد وأنت مستعد لما يلي:

تعرَّف على كيفية التعامل مع حالات الاستخدام الأساسية، وتعرَّف على سير عمل تطوير القواعد واختبارها ونشرها: