Firebase Security Rules از زبانهای انعطافپذیر، قدرتمند و سفارشی بهره میبرند که طیف وسیعی از پیچیدگی و جزئیات را پشتیبانی میکنند. شما میتوانید Rules خود را تا حد امکان خاص یا عمومی برای برنامه خود تنظیم کنید. قوانین Realtime Database از نحوی استفاده میکنند که شبیه جاوا اسکریپت در ساختار JSON است. قوانین Cloud Firestore و Cloud Storage از زبانی مبتنی بر زبان عبارات مشترک (CEL) استفاده میکنند که بر اساس CEL با عبارات match و allow ساخته شده است که از دسترسی مشروط پشتیبانی میکنند.
از آنجا که اینها زبانهای سفارشی هستند، یادگیری آنها کمی زمانبر است. از این راهنما برای درک بهتر زبان Rules استفاده کنید تا بتوانید عمیقتر به قوانین پیچیدهتر بپردازید.
برای کسب اطلاعات بیشتر در مورد قوانین یک محصول، آن را انتخاب کنید.
ساختار پایه
Cloud Firestore
Firebase Security Rules در Cloud Firestore و Cloud Storage از ساختار و سینتکس زیر استفاده میکنند:
service <<name>> {
// Match the resource path.
match <<path>> {
// Allow the request if the following conditions are true.
allow <<methods>> : if <<condition>>
}
}
مفاهیم کلیدی زیر برای درک هنگام تدوین قوانین مهم هستند:
- درخواست (Request): متد یا متدهایی که در دستور
allowفراخوانی میشوند. اینها متدهایی هستند که شما اجازه اجرا شدن آنها را میدهید. متدهای استاندارد عبارتند از:get،list،create،updateوdelete. متدهای مربوط بهreadوwrite، دسترسی گسترده به خواندن و نوشتن را در پایگاه داده یا مسیر ذخیرهسازی مشخص شده فراهم میکنند. - مسیر: پایگاه داده یا محل ذخیرهسازی، که به صورت یک مسیر URI نمایش داده میشود.
- قانون: دستور
allowکه شامل شرطی است که در صورت درست بودن ارزیابی درخواست، آن را مجاز میکند.
هر یک از این مفاهیم با جزئیات بیشتر در زیر توضیح داده شده است.
Cloud Storage
Firebase Security Rules در Cloud Firestore و Cloud Storage از ساختار و سینتکس زیر استفاده میکنند:
service <<name>> {
// Match the resource path.
match <<path>> {
// Allow the request if the following conditions are true.
allow <<methods>> : if <<condition>>
}
}
مفاهیم کلیدی زیر برای درک هنگام تدوین قوانین مهم هستند:
- درخواست (Request): متد یا متدهایی که در دستور
allowفراخوانی میشوند. اینها متدهایی هستند که شما اجازه اجرا شدن آنها را میدهید. متدهای استاندارد عبارتند از:get،list،create،updateوdelete. متدهای مربوط بهreadوwrite، دسترسی گسترده به خواندن و نوشتن را در پایگاه داده یا مسیر ذخیرهسازی مشخص شده فراهم میکنند. - مسیر: پایگاه داده یا محل ذخیرهسازی، که به صورت یک مسیر URI نمایش داده میشود.
- قانون: دستور
allowکه شامل شرطی است که در صورت درست بودن ارزیابی درخواست، آن را مجاز میکند.
هر یک از این مفاهیم با جزئیات بیشتر در زیر توضیح داده شده است.
Realtime Database
در Realtime Database ، Firebase Security Rules شامل عباراتی شبیه به جاوا اسکریپت هستند که در یک سند JSON قرار دارند.
آنها از سینتکس زیر استفاده میکنند:
{
"rules": {
"<<path>>": {
// Allow the request if the condition for each method is true.
".read": <<condition>>,
".write": <<condition>>,
".validate": <<condition>>
}
}
}
سه عنصر اساسی در این قاعده وجود دارد:
- مسیر: مکان پایگاه داده. این ساختار JSON پایگاه داده شما را منعکس میکند.
- درخواست: اینها روشهایی هستند که قانون برای اعطای دسترسی استفاده میکند. قوانین
readوwrite، دسترسی گسترده خواندن و نوشتن را اعطا میکنند، در حالی که قوانینvalidateبه عنوان یک تأیید ثانویه برای اعطای دسترسی بر اساس دادههای ورودی یا موجود عمل میکنند. - شرط: شرطی که در صورت درست بودن ارزیابی درخواست، آن را مجاز میکند.
ساختارهای قانون
Cloud Firestore
عناصر اساسی یک قانون در Cloud Firestore و Cloud Storage به شرح زیر است:
- اعلان
service: محصول Firebase که قوانین روی آن اعمال میشود را اعلان میکند. - بلوک
match: مسیری را در پایگاه داده یا مخزن ذخیرهسازی که قوانین روی آن اعمال میشوند، تعریف میکند. - دستور
allow: شرایطی را برای اعطای دسترسی فراهم میکند که بر اساس متدها از هم متمایز میشوند. متدهای پشتیبانی شده عبارتند از:get،list،create،update،deleteو متدهای کمکیreadوwrite. - اعلانهای اختیاری
function: امکان ترکیب و پوشش شرایط برای استفاده در چندین قانون را فراهم میکند.
این service شامل یک یا چند بلوک match با دستورات allow است که شرایطی را برای اعطای دسترسی به درخواستها فراهم میکنند. متغیرهای request و resource برای استفاده در شرایط قانون در دسترس هستند. زبان Firebase Security Rules همچنین از اعلانهای function پشتیبانی میکند.
نسخه نحوی
عبارت syntax نسخه زبان Firebase Rules مورد استفاده برای نوشتن سورس را نشان میدهد. آخرین نسخه این زبان v2 است.
rules_version = '2';
service cloud.firestore {
...
}
اگر هیچ دستور rules_version ارائه نشود، قوانین شما با استفاده از موتور v1 ارزیابی خواهند شد.
خدمات
تعریف service مشخص میکند که قوانین شما روی کدام محصول یا سرویس Firebase اعمال میشود. شما فقط میتوانید یک تعریف service در هر فایل منبع قرار دهید.
Cloud Firestore
service cloud.firestore {
// Your 'match' blocks with their corresponding 'allow' statements and
// optional 'function' declarations are contained here
}
Cloud Storage
service firebase.storage {
// Your 'match' blocks with their corresponding 'allow' statements and
// optional 'function' declarations are contained here
}
اگر با استفاده از Firebase CLI قوانینی را برای Cloud Firestore و Cloud Storage تعریف میکنید، باید آنها را در فایلهای جداگانه نگهداری کنید.
مطابقت
یک بلوک match ، الگوی path را اعلام میکند که با مسیر عملیات درخواستی ( request.path ورودی) تطبیق داده میشود. بدنه match باید دارای یک یا چند بلوک match در تو، دستورات allow یا اعلان function باشد. مسیر در بلوکهای match تو در تو، نسبت به مسیر در بلوک match والد است.
الگوی path ، نامی شبیه به دایرکتوری است که ممکن است شامل متغیرها یا کاراکترهای جایگزین باشد. الگوی path ، امکان تطبیق قطعه تک مسیری و قطعه چند مسیری را فراهم میکند. هر متغیری که در یک path قرار دارد، در محدوده match یا هر محدوده تو در تو که path در آن تعریف شده است، قابل مشاهده است.
تطابقها با یک الگوی path میتوانند جزئی یا کامل باشند:
- تطابقهای جزئی: الگوی
path، یک تطابق پیشوندی ازrequest.pathاست. - تطابق کامل: الگوی
pathبا کلrequest.pathمطابقت دارد.
وقتی یک تطابق کامل انجام میشود، قوانین درون بلوک ارزیابی میشوند. وقتی یک تطابق جزئی انجام میشود، قوانین match تودرتو آزمایش میشوند تا مشخص شود که آیا هیچ path تودرتویی تطابق را کامل میکند یا خیر.
قوانین موجود در هر match کامل ارزیابی میشوند تا مشخص شود که آیا درخواست مجاز است یا خیر. اگر هر قانون منطبق، دسترسی را اعطا کند، درخواست مجاز است. اگر هیچ قانون منطبقی دسترسی را اعطا نکند، درخواست رد میشود.
// Given request.path == /example/hello/nested/path the following
// declarations indicate whether they are a partial or complete match and
// the value of any variables visible within the scope.
service firebase.storage {
// Partial match.
match /example/{singleSegment} { // `singleSegment` == 'hello'
allow write; // Write rule not evaluated.
// Complete match.
match /nested/path { // `singleSegment` visible in scope.
allow read; // Read rule is evaluated.
}
}
// Complete match.
match /example/{multiSegment=**} { // `multiSegment` == /hello/nested/path
allow read; // Read rule is evaluated.
}
}
همانطور که مثال بالا نشان میدهد، تعریف path از متغیرهای زیر پشتیبانی میکند:
- یک متغیر wildcard تک قسمتی: یک متغیر wildcard با قرار دادن یک متغیر در داخل آکولادهای مجعد در یک مسیر تعریف میشود:
{variable}. این متغیر در داخل دستورmatchبه صورت یکstringقابل دسترسی است. - علامت بازگشتی: علامت بازگشتی یا چندبخشی با چندین بخش مسیر در یا زیر یک مسیر مطابقت دارد. این علامت با تمام مسیرهای زیر مکانی که شما آن را تنظیم کردهاید مطابقت دارد. میتوانید آن را با اضافه کردن رشته
=**در انتهای متغیر قطعه خود تعریف کنید:{variable=**}. این متغیر در داخل دستورmatchبه عنوان یک شیءpathقابل دسترسی است.
اجازه دادن
بلوک match شامل یک یا چند دستور allow است. اینها قوانین واقعی شما هستند. میتوانید قوانین allow را به یک یا چند متد اعمال کنید. شرایط یک دستور allow باید درست باشد تا Cloud Firestore یا Cloud Storage هر درخواست ورودی را اعطا کنند. همچنین میتوانید دستورات allow را بدون شرط بنویسید، به عنوان مثال، allow read . با این حال، اگر دستور allow شامل شرطی نباشد، همیشه درخواست آن متد را مجاز میداند.
اگر هر یک از قوانین allow برای متد برقرار باشد، درخواست مجاز است. علاوه بر این، اگر یک قانون کلیتر دسترسی را اعطا کند، Rules دسترسی را اعطا میکند و هرگونه قانون جزئیتر که ممکن است دسترسی را محدود کند، نادیده میگیرد.
مثال زیر را در نظر بگیرید، که در آن هر کاربری میتواند هر یک از فایلهای خود را بخواند یا حذف کند. یک قانون جزئیتر فقط در صورتی اجازه نوشتن میدهد که کاربر درخواستکنندهی نوشتن، مالک فایل باشد و فایل PNG باشد. یک کاربر میتواند هر فایلی را در مسیر فرعی حذف کند - حتی اگر PNG نباشند - زیرا قانون قبلی این اجازه را میدهد.
service firebase.storage {
// Allow the requestor to read or delete any resource on a path under the
// user directory.
match /users/{userId}/{anyUserFile=**} {
allow read, delete: if request.auth != null && request.auth.uid == userId;
}
// Allow the requestor to create or update their own images.
// When 'request.method' == 'delete' this rule and the one matching
// any path under the user directory would both match and the `delete`
// would be permitted.
match /users/{userId}/images/{imageId} {
// Whether to permit the request depends on the logical OR of all
// matched rules. This means that even if this rule did not explicitly
// allow the 'delete' the earlier rule would have.
allow write: if request.auth != null && request.auth.uid == userId && imageId.matches('*.png');
}
}
روش
هر دستور allow شامل متدی است که دسترسی به درخواستهای ورودی از همان متد را اعطا میکند.
| روش | نوع درخواست |
|---|---|
| روشهای راحتی | |
read | هر نوع درخواست خواندن |
write | هر نوع درخواست نوشتن |
| روشهای استاندارد | |
get | درخواستهای خواندن برای اسناد یا فایلهای تکی |
list | درخواستهای مربوط به پرسشها و مجموعهها را بخوانید |
create | نوشتن اسناد یا فایلهای جدید |
update | نوشتن در اسناد پایگاه داده موجود یا بهروزرسانی فرادادههای فایل |
delete | حذف دادهها |
شما نمیتوانید متدهای خواندن را در یک بلوک match block) با هم همپوشانی داشته باشید یا متدهای نوشتن را در یک path تعریف شده با هم تداخل دهید.
برای مثال، قوانین زیر با شکست مواجه میشوند:
service bad.example {
match /rules/with/overlapping/methods {
// This rule allows reads to all authenticated users
allow read: if request.auth != null;
match another/subpath {
// This secondary, more specific read rule causes an error
allow get: if request.auth != null && request.auth.uid == "me";
// Overlapping write methods in the same path cause an error as well
allow write: if request.auth != null;
allow create: if request.auth != null && request.auth.uid == "me";
}
}
}
عملکرد
با پیچیدهتر شدن قوانین امنیتی شما، ممکن است بخواهید مجموعهای از شرایط را در توابعی قرار دهید که بتوانید در سراسر مجموعه قوانین خود از آنها استفاده مجدد کنید. قوانین امنیتی از توابع سفارشی پشتیبانی میکنند. نحو توابع سفارشی کمی شبیه جاوا اسکریپت است، اما توابع قوانین امنیتی به زبانی خاص دامنه نوشته میشوند که محدودیتهای مهمی دارد:
- توابع میتوانند فقط شامل یک دستور
returnباشند. آنها نمیتوانند هیچ منطق اضافی داشته باشند. برای مثال، آنها نمیتوانند حلقهها را اجرا کنند یا سرویسهای خارجی را فراخوانی کنند. - توابع میتوانند به طور خودکار به توابع و متغیرها از محدودهای که در آن تعریف شدهاند دسترسی داشته باشند. برای مثال، تابعی که در محدوده
service cloud.firestoreتعریف شده است، به متغیرresourceو توابع داخلی مانندget()وexists()دسترسی دارد. - توابع میتوانند توابع دیگر را فراخوانی کنند اما نمیتوانند به صورت بازگشتی عمل کنند. عمق کل پشته فراخوانی به 20 محدود شده است.
- در نسخه
v2قوانین، توابع میتوانند متغیرها را با استفاده از کلمه کلیدیletتعریف کنند. توابع میتوانند تا 10 اتصال let داشته باشند، اما باید با یک دستور return خاتمه یابند.
یک تابع با کلمه کلیدی function تعریف میشود و هیچ یا چند آرگومان میگیرد. برای مثال، ممکن است بخواهید دو نوع شرط استفاده شده در مثالهای بالا را در یک تابع واحد ترکیب کنید:
service cloud.firestore {
match /databases/{database}/documents {
// 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 /cities/{city} {
allow read, write: if signedInOrPublic();
}
match /users/{user} {
allow read, write: if signedInOrPublic();
}
}
}
در اینجا مثالی آورده شده است که آرگومانهای تابع و انتسابهای let را نشان میدهد. دستورات انتساب let باید با نقطه ویرگول از هم جدا شوند.
function isAuthorOrAdmin(userId, article) {
let isAuthor = article.author == userId;
let isAdmin = exists(/databases/$(database)/documents/admins/$(userId));
return isAuthor || isAdmin;
}
توجه داشته باشید که چگونه انتساب isAdmin جستجوی مجموعه admins را اجباری میکند. برای ارزیابی تنبل بدون نیاز به جستجوهای غیرضروری، از ماهیت اتصال کوتاه مقایسههای && (AND) و || (OR) برای فراخوانی تابع دوم فقط در صورتی که isAuthor درست (برای مقایسههای && ) یا نادرست (برای مقایسههای || ) نشان داده شود، استفاده کنید.
function isAdmin(userId) {
return exists(/databases/$(database)/documents/admins/$(userId));
}
function isAuthorOrAdmin(userId, article) {
let isAuthor = article.author == userId;
// `||` is short-circuiting; isAdmin called only if isAuthor == false.
return isAuthor || isAdmin(userId);
}
استفاده از توابع در قوانین امنیتی شما، با افزایش پیچیدگی قوانین، آنها را قابل نگهداریتر میکند.
Cloud Storage
عناصر اساسی یک قانون در Cloud Firestore و Cloud Storage به شرح زیر است:
- اعلان
service: محصول Firebase که قوانین روی آن اعمال میشود را اعلان میکند. - بلوک
match: مسیری را در پایگاه داده یا مخزن ذخیرهسازی که قوانین روی آن اعمال میشوند، تعریف میکند. - دستور
allow: شرایطی را برای اعطای دسترسی فراهم میکند که بر اساس متدها از هم متمایز میشوند. متدهای پشتیبانی شده عبارتند از:get،list،create،update،deleteو متدهای کمکیreadوwrite. - اعلانهای اختیاری
function: امکان ترکیب و پوشش شرایط برای استفاده در چندین قانون را فراهم میکند.
این service شامل یک یا چند بلوک match با دستورات allow است که شرایطی را برای اعطای دسترسی به درخواستها فراهم میکنند. متغیرهای request و resource برای استفاده در شرایط قانون در دسترس هستند. زبان Firebase Security Rules همچنین از اعلانهای function پشتیبانی میکند.
نسخه نحوی
عبارت syntax نسخه زبان Firebase Rules مورد استفاده برای نوشتن سورس را نشان میدهد. آخرین نسخه این زبان v2 است.
rules_version = '2';
service cloud.firestore {
...
}
اگر هیچ دستور rules_version ارائه نشود، قوانین شما با استفاده از موتور v1 ارزیابی خواهند شد.
خدمات
تعریف service ، مشخص میکند که قوانین شما روی کدام محصول یا سرویس فایربیس اعمال میشود. شما فقط میتوانید یک تعریف service در هر فایل منبع قرار دهید.
Cloud Firestore
service cloud.firestore {
// Your 'match' blocks with their corresponding 'allow' statements and
// optional 'function' declarations are contained here
}
Cloud Storage
service firebase.storage {
// Your 'match' blocks with their corresponding 'allow' statements and
// optional 'function' declarations are contained here
}
اگر با استفاده از Firebase CLI قوانینی را برای Cloud Firestore و Cloud Storage تعریف میکنید، باید آنها را در فایلهای جداگانه نگهداری کنید.
مطابقت
یک بلوک match ، الگوی path را اعلام میکند که با مسیر عملیات درخواستی ( request.path ورودی) تطبیق داده میشود. بدنه match باید دارای یک یا چند بلوک match در تو، دستورات allow یا اعلان function باشد. مسیر در بلوکهای match تو در تو، نسبت به مسیر در بلوک match والد است.
الگوی path ، نامی شبیه به دایرکتوری است که ممکن است شامل متغیرها یا کاراکترهای جایگزین باشد. الگوی path ، امکان تطبیق قطعه تک مسیری و قطعه چند مسیری را فراهم میکند. هر متغیری که در یک path قرار دارد، در محدوده match یا هر محدوده تو در تو که path در آن تعریف شده است، قابل مشاهده است.
تطابقها با یک الگوی path میتوانند جزئی یا کامل باشند:
- تطابقهای جزئی: الگوی
path، یک تطابق پیشوندی ازrequest.pathاست. - تطابق کامل: الگوی
pathبا کلrequest.pathمطابقت دارد.
وقتی یک تطابق کامل انجام میشود، قوانین درون بلوک ارزیابی میشوند. وقتی یک تطابق جزئی انجام میشود، قوانین match تودرتو آزمایش میشوند تا مشخص شود که آیا هیچ path تودرتویی تطابق را کامل میکند یا خیر.
قوانین موجود در هر match کامل ارزیابی میشوند تا مشخص شود که آیا درخواست مجاز است یا خیر. اگر هر قانون منطبق، دسترسی را اعطا کند، درخواست مجاز است. اگر هیچ قانون منطبقی دسترسی را اعطا نکند، درخواست رد میشود.
// Given request.path == /example/hello/nested/path the following
// declarations indicate whether they are a partial or complete match and
// the value of any variables visible within the scope.
service firebase.storage {
// Partial match.
match /example/{singleSegment} { // `singleSegment` == 'hello'
allow write; // Write rule not evaluated.
// Complete match.
match /nested/path { // `singleSegment` visible in scope.
allow read; // Read rule is evaluated.
}
}
// Complete match.
match /example/{multiSegment=**} { // `multiSegment` == /hello/nested/path
allow read; // Read rule is evaluated.
}
}
همانطور که مثال بالا نشان میدهد، تعریف path از متغیرهای زیر پشتیبانی میکند:
- یک متغیر wildcard تک قسمتی: یک متغیر wildcard با قرار دادن یک متغیر در داخل آکولادهای مجعد در یک مسیر تعریف میشود:
{variable}. این متغیر در داخل دستورmatchبه صورت یکstringقابل دسترسی است. - علامت بازگشتی: علامت بازگشتی یا چندبخشی با چندین بخش مسیر در یا زیر یک مسیر مطابقت دارد. این علامت با تمام مسیرهای زیر مکانی که شما آن را تنظیم کردهاید مطابقت دارد. میتوانید آن را با اضافه کردن رشته
=**در انتهای متغیر قطعه خود تعریف کنید:{variable=**}. این متغیر در داخل دستورmatchبه عنوان یک شیءpathقابل دسترسی است.
اجازه دادن
بلوک match شامل یک یا چند دستور allow است. اینها قوانین واقعی شما هستند. میتوانید قوانین allow را به یک یا چند متد اعمال کنید. شرایط یک دستور allow باید درست باشد تا Cloud Firestore یا Cloud Storage هر درخواست ورودی را اعطا کنند. همچنین میتوانید دستورات allow را بدون شرط بنویسید، به عنوان مثال، allow read . با این حال، اگر دستور allow شامل شرطی نباشد، همیشه درخواست آن متد را مجاز میداند.
اگر هر یک از قوانین allow برای متد برقرار باشد، درخواست مجاز است. علاوه بر این، اگر یک قانون کلیتر دسترسی را اعطا کند، Rules دسترسی را اعطا میکند و هرگونه قانون جزئیتر که ممکن است دسترسی را محدود کند، نادیده میگیرد.
مثال زیر را در نظر بگیرید، که در آن هر کاربری میتواند هر یک از فایلهای خود را بخواند یا حذف کند. یک قانون جزئیتر فقط در صورتی اجازه نوشتن میدهد که کاربر درخواستکنندهی نوشتن، مالک فایل باشد و فایل PNG باشد. یک کاربر میتواند هر فایلی را در مسیر فرعی حذف کند - حتی اگر PNG نباشند - زیرا قانون قبلی این اجازه را میدهد.
service firebase.storage {
// Allow the requestor to read or delete any resource on a path under the
// user directory.
match /users/{userId}/{anyUserFile=**} {
allow read, delete: if request.auth != null && request.auth.uid == userId;
}
// Allow the requestor to create or update their own images.
// When 'request.method' == 'delete' this rule and the one matching
// any path under the user directory would both match and the `delete`
// would be permitted.
match /users/{userId}/images/{imageId} {
// Whether to permit the request depends on the logical OR of all
// matched rules. This means that even if this rule did not explicitly
// allow the 'delete' the earlier rule would have.
allow write: if request.auth != null && request.auth.uid == userId && imageId.matches('*.png');
}
}
روش
هر دستور allow شامل متدی است که دسترسی به درخواستهای ورودی از همان متد را اعطا میکند.
| روش | نوع درخواست |
|---|---|
| روشهای راحتی | |
read | هر نوع درخواست خواندن |
write | هر نوع درخواست نوشتن |
| روشهای استاندارد | |
get | درخواستهای خواندن برای اسناد یا فایلهای تکی |
list | درخواستهای مربوط به پرسشها و مجموعهها را بخوانید |
create | نوشتن اسناد یا فایلهای جدید |
update | نوشتن در اسناد پایگاه داده موجود یا بهروزرسانی فرادادههای فایل |
delete | حذف دادهها |
شما نمیتوانید متدهای خواندن را در یک بلوک match block) با هم همپوشانی داشته باشید یا متدهای نوشتن را در یک path تعریف شده با هم تداخل دهید.
برای مثال، قوانین زیر با شکست مواجه میشوند:
service bad.example {
match /rules/with/overlapping/methods {
// This rule allows reads to all authenticated users
allow read: if request.auth != null;
match another/subpath {
// This secondary, more specific read rule causes an error
allow get: if request.auth != null && request.auth.uid == "me";
// Overlapping write methods in the same path cause an error as well
allow write: if request.auth != null;
allow create: if request.auth != null && request.auth.uid == "me";
}
}
}
عملکرد
با پیچیدهتر شدن قوانین امنیتی شما، ممکن است بخواهید مجموعهای از شرایط را در توابعی قرار دهید که بتوانید در سراسر مجموعه قوانین خود از آنها استفاده مجدد کنید. قوانین امنیتی از توابع سفارشی پشتیبانی میکنند. نحو توابع سفارشی کمی شبیه جاوا اسکریپت است، اما توابع قوانین امنیتی به زبانی خاص دامنه نوشته میشوند که محدودیتهای مهمی دارد:
- توابع میتوانند فقط شامل یک دستور
returnباشند. آنها نمیتوانند هیچ منطق اضافی داشته باشند. برای مثال، آنها نمیتوانند حلقهها را اجرا کنند یا سرویسهای خارجی را فراخوانی کنند. - توابع میتوانند به طور خودکار به توابع و متغیرها از محدودهای که در آن تعریف شدهاند دسترسی داشته باشند. برای مثال، تابعی که در محدوده
service cloud.firestoreتعریف شده است، به متغیرresourceو توابع داخلی مانندget()وexists()دسترسی دارد. - توابع میتوانند توابع دیگر را فراخوانی کنند اما نمیتوانند به صورت بازگشتی عمل کنند. عمق کل پشته فراخوانی به 20 محدود شده است.
- در نسخه
v2قوانین، توابع میتوانند متغیرها را با استفاده از کلمه کلیدیletتعریف کنند. توابع میتوانند تا 10 اتصال let داشته باشند، اما باید با یک دستور return خاتمه یابند.
یک تابع با کلمه کلیدی function تعریف میشود و هیچ یا چند آرگومان میگیرد. برای مثال، ممکن است بخواهید دو نوع شرط استفاده شده در مثالهای بالا را در یک تابع واحد ترکیب کنید:
service cloud.firestore {
match /databases/{database}/documents {
// 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 /cities/{city} {
allow read, write: if signedInOrPublic();
}
match /users/{user} {
allow read, write: if signedInOrPublic();
}
}
}
در اینجا مثالی آورده شده است که آرگومانهای تابع و انتسابهای let را نشان میدهد. دستورات انتساب let باید با نقطه ویرگول از هم جدا شوند.
function isAuthorOrAdmin(userId, article) {
let isAuthor = article.author == userId;
let isAdmin = exists(/databases/$(database)/documents/admins/$(userId));
return isAuthor || isAdmin;
}
توجه داشته باشید که چگونه انتساب isAdmin جستجوی مجموعه admins را اجباری میکند. برای ارزیابی تنبل بدون نیاز به جستجوهای غیرضروری، از ماهیت اتصال کوتاه مقایسههای && (AND) و || (OR) برای فراخوانی تابع دوم فقط در صورتی که isAuthor درست (برای مقایسههای && ) یا نادرست (برای مقایسههای || ) نشان داده شود، استفاده کنید.
function isAdmin(userId) {
return exists(/databases/$(database)/documents/admins/$(userId));
}
function isAuthorOrAdmin(userId, article) {
let isAuthor = article.author == userId;
// `||` is short-circuiting; isAdmin called only if isAuthor == false.
return isAuthor || isAdmin(userId);
}
استفاده از توابع در قوانین امنیتی شما، با افزایش پیچیدگی قوانین، آنها را قابل نگهداریتر میکند.
Realtime Database
همانطور که در بالا ذکر شد، Rules Realtime Database شامل سه عنصر اساسی هستند: مکان پایگاه داده به عنوان آینهای از ساختار JSON پایگاه داده، نوع درخواست و شرط اعطای دسترسی.
محل پایگاه داده
ساختار قوانین شما باید از ساختار دادههایی که در پایگاه داده خود ذخیره کردهاید، پیروی کند. برای مثال، در یک برنامه چت با لیستی از پیامها، ممکن است دادههایی به شکل زیر داشته باشید:
{
"messages": {
"message0": {
"content": "Hello",
"timestamp": 1405704370369
},
"message1": {
"content": "Goodbye",
"timestamp": 1405704395231
},
...
}
}
قوانین شما باید آن ساختار را منعکس کنند. برای مثال:
{
"rules": {
"messages": {
"$message": {
// only messages from the last ten minutes can be read
".read": "data.child('timestamp').val() > (now - 600000)",
// new messages must have a string content and a number timestamp
".validate": "newData.hasChildren(['content', 'timestamp']) &&
newData.child('content').isString() &&
newData.child('timestamp').isNumber()"
}
}
}
}
همانطور که مثال بالا نشان میدهد، Rules Realtime Database از متغیر $location برای تطبیق بخشهای مسیر پشتیبانی میکنند. از پیشوند $ قبل از بخش مسیر خود استفاده کنید تا قانون خود را با هر گره فرزند در طول مسیر تطبیق دهید.
{
"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')"
}
}
}
}
}
همچنین میتوانید از $variable به صورت موازی با نام مسیرهای ثابت استفاده کنید.
{
"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 }
}
}
}
روش
در Realtime Database ، سه نوع قانون وجود دارد. دو نوع از این قوانین - read و write - بر روی متد یک درخواست ورودی اعمال میشوند. نوع قانون validate ساختارهای داده را اعمال کرده و قالب و محتوای دادهها را اعتبارسنجی میکند. Rules قوانین .validate را پس از تأیید اعطای دسترسی توسط یک قانون .write اجرا میکنند.
| انواع قوانین | |
|---|---|
| .خواندن | شرح میدهد که آیا و چه زمانی کاربران اجازه خواندن دادهها را دارند. |
| .نوشتن | شرح میدهد که آیا و چه زمانی اجازه نوشتن دادهها وجود دارد. |
| اعتبارسنجی | تعریف میکند که یک مقدار با قالببندی صحیح چگونه خواهد بود، آیا دارای ویژگیهای فرزند است یا خیر، و نوع داده را نیز مشخص میکند. |
به طور پیشفرض، اگر قانونی برای اجازه دادن به آن وجود نداشته باشد، دسترسی به یک مسیر رد میشود.
شرایط ساختمان
Cloud Firestore
یک شرط، یک عبارت بولی است که تعیین میکند آیا یک عملیات خاص باید مجاز یا رد شود. متغیرهای request و resource ، زمینه را برای این شرایط فراهم میکنند.
متغیر request
متغیر request شامل فیلدهای زیر و اطلاعات مربوطه است:
request.auth
یک JSON Web Token (JWT) که شامل اعتبارنامههای احراز هویت از Firebase Authentication است. auth token شامل مجموعهای از ادعاهای استاندارد و هرگونه ادعای سفارشی است که شما از طریق Firebase Authentication ایجاد میکنید. درباره Firebase Security Rules و Authentication بیشتر بدانید.
request.method
request.method میتواند هر یک از متدهای استاندارد یا یک متد سفارشی باشد. متدهای راحتی read و write نیز برای سادهسازی قوانین نوشتن وجود دارند که به ترتیب برای همه متدهای استاندارد فقط خواندنی یا فقط نوشتنی اعمال میشوند.
request.params
request.params شامل هر دادهای است که به طور خاص به request.resource مربوط نمیشود و ممکن است برای ارزیابی مفید باشد. در عمل، این نقشه باید برای همه متدهای استاندارد خالی باشد و باید حاوی دادههای غیر منبع برای متدهای سفارشی باشد. سرویسها باید مراقب باشند که نوع هیچ یک از کلیدها و مقادیر ارائه شده به عنوان params را تغییر نام یا اصلاح نکنند.
request.path
request.path مسیر resource هدف است. این مسیر نسبت به سرویس است. بخشهایی از مسیر که حاوی کاراکترهای non-url safe مانند / هستند، url-encoded میشوند.
متغیر resource
resource ، مقدار فعلی درون سرویس است که به صورت نقشهای از جفتهای کلید-مقدار نمایش داده میشود. ارجاع به resource درون یک شرط، حداکثر منجر به یک بار خواندن مقدار از سرویس خواهد شد. این جستجو در برابر هرگونه سهمیه مربوط به سرویس برای منبع محاسبه میشود. برای درخواستهای get ، resource فقط در صورت رد شدن، در سهمیه محاسبه میشود.
عملگرها و اولویت عملگرها
از جدول زیر به عنوان مرجع برای اپراتورها و اولویت مربوط به آنها در Rules مربوط به Cloud Firestore و Cloud Storage استفاده کنید.
با توجه به عبارات دلخواه a و b ، یک فیلد f و یک اندیس i .
| اپراتور | توضیحات | شرکتپذیری |
|---|---|---|
a[i] a() af | فهرست، فراخوانی، دسترسی به فیلد | چپ به راست | !a -a | نفی تکوجهی | راست به چپ |
a/ba%ba*b | عملگرهای ضربی | چپ به راست |
a+b ab | عملگرهای افزایشی | چپ به راست |
a>ba>=ba | عملگرهای رابطهای | چپ به راست |
a in b | وجود در لیست یا نقشه | چپ به راست |
a is type | مقایسه نوع، که در آن type میتواند bool، int، float، number، string، list، map، timestamp، duration، path یا latlng باشد. | چپ به راست |
a==ba!=b | عملگرهای مقایسهای | چپ به راست | a && b | شرطی و | چپ به راست |
a || b | یا شرطی | چپ به راست |
a ? true_value : false_value | عبارت سهتایی | چپ به راست |
Cloud Storage
یک شرط، یک عبارت بولی است که تعیین میکند آیا یک عملیات خاص باید مجاز یا رد شود. متغیرهای request و resource ، زمینه را برای این شرایط فراهم میکنند.
متغیر request
متغیر request شامل فیلدهای زیر و اطلاعات مربوطه است:
request.auth
یک JSON Web Token (JWT) که حاوی اعتبارنامههای احراز هویت از Firebase Authentication است. auth token شامل مجموعهای از ادعاهای استاندارد و هرگونه ادعای سفارشی است که شما از طریق Firebase Authentication ایجاد میکنید. درباره Firebase Security Rules و Authentication بیشتر بدانید.
request.method
request.method میتواند هر یک از متدهای استاندارد یا یک متد سفارشی باشد. متدهای راحتی read و write نیز برای سادهسازی قوانین نوشتن وجود دارند که به ترتیب برای همه متدهای استاندارد فقط خواندنی یا فقط نوشتنی اعمال میشوند.
request.params
request.params شامل هر دادهای است که به طور خاص به request.resource مربوط نمیشود و ممکن است برای ارزیابی مفید باشد. در عمل، این نقشه باید برای همه متدهای استاندارد خالی باشد و باید حاوی دادههای غیر منبع برای متدهای سفارشی باشد. سرویسها باید مراقب باشند که نوع هیچ یک از کلیدها و مقادیر ارائه شده به عنوان params را تغییر نام یا اصلاح نکنند.
request.path
request.path مسیر resource هدف است. این مسیر نسبت به سرویس است. بخشهایی از مسیر که حاوی کاراکترهای non-url safe مانند / هستند، url-encoded میشوند.
متغیر resource
resource ، مقدار فعلی درون سرویس است که به صورت نقشهای از جفتهای کلید-مقدار نمایش داده میشود. ارجاع به resource درون یک شرط، حداکثر منجر به یک بار خواندن مقدار از سرویس خواهد شد. این جستجو در برابر هرگونه سهمیه مربوط به سرویس برای منبع محاسبه میشود. برای درخواستهای get ، resource فقط در صورت رد شدن، در سهمیه محاسبه میشود.
عملگرها و اولویت عملگرها
از جدول زیر به عنوان مرجع برای اپراتورها و اولویت مربوط به آنها در Rules مربوط به Cloud Firestore و Cloud Storage استفاده کنید.
با توجه به عبارات دلخواه a و b ، یک فیلد f و یک اندیس i .
| اپراتور | توضیحات | شرکتپذیری |
|---|---|---|
a[i] a() af | فهرست، فراخوانی، دسترسی به فیلد | چپ به راست | !a -a | نفی تکوجهی | راست به چپ |
a/ba%ba*b | عملگرهای ضربی | چپ به راست |
a+b ab | عملگرهای افزایشی | چپ به راست |
a>ba>=ba | عملگرهای رابطهای | چپ به راست |
a in b | وجود در لیست یا نقشه | چپ به راست |
a is type | مقایسه نوع، که در آن type میتواند bool، int، float، number، string، list، map، timestamp، duration، path یا latlng باشد. | چپ به راست |
a==ba!=b | عملگرهای مقایسهای | چپ به راست | a && b | شرطی و | چپ به راست |
a || b | یا شرطی | چپ به راست |
a ? true_value : false_value | عبارت سهتایی | چپ به راست |
Realtime Database
یک شرط، یک عبارت بولی است که تعیین میکند آیا یک عملیات خاص باید مجاز یا رد شود. میتوانید این شرطها را در Rules Realtime Database به روشهای زیر تعریف کنید.
متغیرهای از پیش تعریف شده
تعدادی متغیر مفید و از پیش تعریف شده وجود دارد که میتوان در تعریف یک قانون به آنها دسترسی داشت. در اینجا خلاصهای از هر یک آمده است:
| متغیرهای از پیش تعریف شده | |
|---|---|
| الان | زمان فعلی از زمان آغاز لینوکس (Linux epoch) به میلیثانیه. این به ویژه برای اعتبارسنجی مهرهای زمانی ایجاد شده با firebase.database.ServerValue.TIMESTAMP مربوط به SDK خوب عمل میکند. |
| ریشه | یک RuleDataSnapshot که مسیر ریشه را در پایگاه داده Firebase، همانطور که قبل از عملیات انجام شده وجود داشته است، نشان میدهد. |
| نیودیتا | یک RuleDataSnapshot که دادهها را به شکلی که پس از عملیات انجام شده وجود خواهند داشت، نشان میدهد. این شامل دادههای جدید در حال نوشتن و دادههای موجود است. |
| دادهها | یک RuleDataSnapshot که دادهها را به شکلی که قبل از عملیات وجود داشتهاند، نشان میدهد. |
| متغیرهای $ | یک مسیر wildcard که برای نمایش شناسهها و کلیدهای فرزند پویا استفاده میشود. |
| نویسنده | نشاندهندهی بار دادهی توکن یک کاربر احراز هویت شده است. |
این متغیرها میتوانند در هر جایی از قوانین شما استفاده شوند. برای مثال، قوانین امنیتی زیر تضمین میکنند که دادههای نوشته شده در گره /foo/ باید رشتهای کمتر از ۱۰۰ کاراکتر باشند:
{
"rules": {
"foo": {
// /foo is readable by the world
".read": true,
// /foo is writable by the world
".write": true,
// data written to /foo must be a string less than 100 characters
".validate": "newData.isString() && newData.val().length < 100"
}
}
}قوانین مبتنی بر داده
هر دادهای در پایگاه داده شما میتواند در قوانین شما استفاده شود. با استفاده از متغیرهای از پیش تعریف شده root ، data و newData ، میتوانید به هر مسیری که قبل یا بعد از یک رویداد نوشتن وجود دارد، دسترسی داشته باشید.
این مثال را در نظر بگیرید که عملیات نوشتن را تا زمانی که مقدار گره /allow_writes/ true باشد، مجاز میداند، گره والد پرچم readOnly ندارد و یک فرزند به نام foo در دادههای تازه نوشته شده وجود دارد:
".write": "root.child('allow_writes').val() === true &&
!data.parent().child('readOnly').exists() &&
newData.child('foo').exists()"قوانین مبتنی بر پرس و جو
اگرچه نمیتوانید از قوانین به عنوان فیلتر استفاده کنید، اما میتوانید با استفاده از پارامترهای پرسوجو در قوانین خود، دسترسی به زیرمجموعههایی از دادهها را محدود کنید. از عبارات query. در قوانین خود برای اعطای دسترسی خواندن یا نوشتن بر اساس پارامترهای پرسوجو استفاده کنید.
برای مثال، قانون مبتنی بر پرسوجوی زیر از قوانین امنیتی مبتنی بر کاربر و قوانین مبتنی بر پرسوجو برای محدود کردن دسترسی به دادههای موجود در مجموعه baskets فقط به سبدهای خریدی که کاربر فعال دارد، استفاده میکند:
"baskets": {
".read": "auth.uid !== null &&
query.orderByChild === 'owner' &&
query.equalTo === auth.uid" // restrict basket access to owner of basket
}
پرسوجوی زیر که شامل پارامترهای پرسوجو در قانون است، موفق خواهد بود:
db.ref("baskets").orderByChild("owner")
.equalTo(auth.currentUser.uid)
.on("value", cb) // Would succeed
با این حال، کوئریهایی که پارامترهای موجود در قانون را شامل نمیشوند، با خطای PermissionDenied مواجه میشوند:
db.ref("baskets").on("value", cb) // Would fail with PermissionDenied
همچنین میتوانید از قوانین مبتنی بر پرسوجو برای محدود کردن میزان دادههایی که یک کلاینت از طریق عملیات خواندن دانلود میکند، استفاده کنید.
برای مثال، قانون زیر دسترسی خواندن را فقط به ۱۰۰۰ نتیجه اول یک پرسوجو، بر اساس اولویت، محدود میکند:
messages: {
".read": "query.orderByKey &&
query.limitToFirst <= 1000"
}
// Example queries:
db.ref("messages").on("value", cb) // Would fail with PermissionDenied
db.ref("messages").limitToFirst(1000)
.on("value", cb) // Would succeed (default order by key)
عبارات query. زیر در Realtime Database Security Rules موجود هستند.
| عبارات قاعده مبتنی بر پرس و جو | ||
|---|---|---|
| بیان | نوع | توضیحات |
| پرس و جو.orderByKey query.orderByPriority پرس و جو.سفارش بر اساس مقدار | بولی | برای پرسوجوهایی که بر اساس کلید، اولویت یا مقدار مرتب شدهاند، مقدار درست (true) و در غیر این صورت مقدار نادرست (false) است. |
| پرس و جو.سفارش توسط فرزند | رشته تهی | از یک رشته برای نمایش مسیر نسبی به یک گره فرزند استفاده کنید. برای مثال، query.orderByChild === "address/zip" . اگر پرسوجو بر اساس گره فرزند مرتب نشده باشد، این مقدار null است. |
| پرس و جو.startAt پرس و جو.پایان query.equalTo | رشته شماره بولی تهی | مرزهای پرسوجوی در حال اجرا را بازیابی میکند، یا اگر هیچ مجموعهای از مرزها وجود نداشته باشد، مقدار null را برمیگرداند. |
| query.limitToFirst query.limitToLast | شماره تهی | محدودیت مربوط به اجرای کوئری را بازیابی میکند، یا اگر محدودیتی تعیین نشده باشد، مقدار null را برمیگرداند. |
اپراتورها
Rules Realtime Database از تعدادی عملگر پشتیبانی میکنند که میتوانید برای ترکیب متغیرها در عبارت شرط از آنها استفاده کنید. لیست کامل عملگرها را در مستندات مرجع مشاهده کنید.
ایجاد شرایط
شرایط واقعی شما بسته به دسترسی که میخواهید اعطا کنید متفاوت خواهد بود. Rules عمداً انعطافپذیری زیادی ارائه میدهند، بنابراین قوانین برنامه شما در نهایت میتوانند به همان اندازه که شما نیاز دارید ساده یا پیچیده باشند.
برای راهنمایی در مورد ایجاد Rules ساده و آماده برای اجرا، به قوانین امنیتی پایه مراجعه کنید.