يستنِد هذا الدليل إلى التعرّف على البنية الأساسية لدليل لغة Firebase Security Rules لتوضيح كيفية إضافة شروط إلى Firebase Security Rules لـ Cloud Storage.
الوحدة الأساسية لـ Cloud Storage Security Rules هي الشرط. العبارة
الشرطية هي تعبير منطقي يحدّد ما إذا كان يجب السماح بإجراء معيّن
أو رفضه. بالنسبة إلى القواعد الأساسية، يعمل استخدام القيم الثابتة true
وfalse
كشرط بشكل جيد جدًا. ولكنّ لغة Firebase Security Rules for Cloud Storage
تمنحك طرقًا لكتابة شروط أكثر تعقيدًا يمكنها:
- التحقق من مصادقة المستخدم
- التحقق من صحة البيانات الواردة
المصادقة
Firebase Security Rules لنظام التشغيل Cloud Storage يدمج مع Firebase Authentication لتوفير مصادقة قوية تستند إلى المستخدمين في Cloud Storage. يتيح ذلك التحكّم في الوصول الدقيق استنادًا إلى المطالبات بالرمز المميز Firebase Authentication.
عندما يُجري مستخدم تمّت مصادقة هويته طلبًا ضد Cloud Storage،
يتمّ تعبئة المتغيّر request.auth
بقيمة uid
(request.auth.uid
) الخاصة بالمستخدم بالإضافة إلى مطالب Firebase Authentication JWT
(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
هي منح مستخدمين individual
أذونات دقيقة على ملفاتهم، بدءًا من تحميل صور الملف الشخصي
ووصولاً إلى قراءة المستندات الخاصة.
بما أنّ الملفات في 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 رمز مميّز مخصّص يحتوي على معلومات إضافية عن أحد أعضاء المجموعة (مثل معرّف المجموعة)
- يجب تضمين معلومات المجموعة (مثل رقم تعريف المجموعة أو قائمة
uid
المعتمَدين) في البيانات الوصفية للملف.
بعد تخزين هذه البيانات في بيانات التعريف الخاصة بالرمز المميّز أو الملف، يمكن الإشارة إليها من داخل قاعدة:
// 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 JWT. وفي حال عدم إجراء ذلك، سيكون
null . |
params |
map<string, string> | خريطة تحتوي على مَعلمات طلب البحث |
path |
المسار | path يمثّل المسار الذي يتم تنفيذ الطلب عليه
|
resource |
map<string, string> | لا تتوفّر قيمة المورد الجديدة إلا في طلبات write .
|
time |
الطابع الزمني | طابع زمني يمثّل وقت الخادم الذي يتم تقييم الطلب فيه |
تقييم الموارد
عند تقييم القواعد، قد تحتاج أيضًا إلى تقييم البيانات الوصفية للملف الذي يتم تحميله أو تنزيله أو تعديله أو حذفه. يتيح لك ذلك إنشاء قواعد معقدة وفعّالة تؤدي إلى تنفيذ إجراءات، مثل السماح فقط بتحميل ملفات ذات أنواع محتوى معيّنة، أو حذف الملفات التي تزيد مساحتها عن حجم معيّن فقط.
Firebase Security Rules لـ Cloud Storage: يوفّر البيانات الوصفية للملف في resource
كائن، الذي يحتوي على أزواج مفتاح/قيمة للبيانات الوصفية التي تظهر في
Cloud Storage كائن. يمكن فحص هذه المواقع في طلبات read
أو
write
لضمان سلامة البيانات.
في طلبات write
(مثل عمليات التحميل وتعديل البيانات الوصفية وعمليات الحذف)، بالإضافة إلى العنصر resource
الذي يحتوي على البيانات الوصفية للملف
المتوفّر حاليًا في مسار الطلب، يمكنك أيضًا استخدام العنصر request.resource
الذي يحتوي على مجموعة فرعية من البيانات الوصفية للملف المطلوب كتابتها إذا كان مسموحًا بالكتابة يمكنك استخدام هاتين القيمتَين لضمان تكامل البيانات
أو فرض قيود التطبيق، مثل نوع الملف أو حجمه.
في ما يلي قائمة كاملة بالسمات في عنصر resource
:
الموقع | النوع | الوصف |
---|---|---|
name |
السلسلة | الاسم الكامل للعنصر |
bucket |
السلسلة | اسم الحزمة التي يتوفّر فيها هذا العنصر |
generation |
int | إنشاء عنصر Google Cloud Storage لهذا العنصر. |
metageneration |
int | Google Cloud Storage الجيل التعريفي للكائن لهذا الكائن. |
size |
int | حجم العنصر بالبايت |
timeCreated |
الطابع الزمني | طابع زمني يمثّل وقت إنشاء عنصر |
updated |
الطابع الزمني | طابع زمني يمثّل وقت آخر تعديل لعنصر |
md5Hash |
السلسلة | تجزئة MD5 للعنصر |
crc32c |
السلسلة | تجزئة crc32c للعنصر |
etag |
السلسلة | علامة etag المرتبطة بهذا العنصر |
contentDisposition |
السلسلة | حالة المحتوى المرتبطة بهذا العنصر |
contentEncoding |
السلسلة | ترميز المحتوى المرتبط بهذا العنصر |
contentLanguage |
السلسلة | لغة المحتوى المرتبطة بهذا العنصر. |
contentType |
السلسلة | نوع المحتوى المرتبط بهذا العنصر |
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)).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 أو Firebase CLI تفعيل الأذونات لربط المنتجَين.
يمكنك إيقاف الميزة من خلال إزالة دور في "إدارة الهوية وإمكانية الوصول"، كما هو موضّح في مقالة إدارة ونشر 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
، والدوالّ المدمَجة مثلget()
وexists()
لـ Cloud Firestore فقط. - يمكن أن تستدعي الدوالّ دوالّ أخرى، ولكن لا يمكنها التكرار. يقتصر إجمالي عمق تسلسل استدعاء الدوال البرمجية على 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 إلى تسهيل صيانتها مع زيادة صعوبة قواعدك.
الخطوات التالية
بعد هذه المناقشة حول الشروط، أصبح لديك فهم أدق للقواعد وأصبحت مستعدًا لإجراء ما يلي:
تعرَّف على كيفية التعامل مع حالات الاستخدام الأساسية، وتعرَّف على سير العمل لتطوير القواعد واختبارها ونشرها:
- اكتب قواعد تتناول السيناريوهات الشائعة.
- ابنِ على معرفتك من خلال مراجعة المواقف التي يجب فيها اكتشاف القواعد غير الآمنة وتجنبها.
- اختبِر القواعد باستخدام محاكي Cloud Storage ومكتبة اختبار قواعد الأمان المخصّصة.
- راجِع الطرق المتاحة لنشر Rules.