«قوانین امنیتی پایگاه داده بیدرنگ Firebase» به شما امکان میدهد دسترسی به دادههای ذخیرهشده در پایگاه دادهتان را کنترل کنید. نحو قوانین انعطافپذیر به شما امکان میدهد قوانینی ایجاد کنید که با هر چیزی مطابقت داشته باشد، از همه نوشتنها در پایگاه داده شما تا عملیات روی گرههای فردی.
«قوانین امنیتی پایگاه داده همزمان» پیکربندی بیانی برای پایگاه داده شما است. این یعنی قوانین جدا از منطق محصول تعریف میشوند. این مزایای متعددی دارد: مشتریان مسئول اجرای امنیت نیستند، پیادهسازیهای دارای اشکال دادههای شما را بهخطر نمیاندازند، و شاید مهمتر از همه، نیازی به داور واسطه، مانند سرور، برای محافظت از دادهها دربرابر جهان نیست.
این موضوع نحو و ساختار پایه «قوانین امنیتی پایگاه داده همزمان» را که برای ایجاد مجموعهقوانین کامل استفاده میشود شرح میدهد.
ساختاردهی قوانین امنیتی شما
«قوانین امنیتی پایگاه داده بیدرنگ» از عبارتهای شبیه جاوا اسکریپت تشکیل شده است که در سند 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. در اینجا
خلاصه کوتاهی از اهداف آنها آورده شده است:
| انواع قانون | |
|---|---|
| .read | توضیح میدهد که آیا کاربران اجازه دارند دادهها را بخوانند و چه زمانی این اجازه را دارند. |
| .write | توضیح میدهد که آیا و چه زمانی دادهها اجازه نوشته شدن دارند. |
| .validate | تعیین میکند که مقدار با قالببندی صحیح چگونه خواهد بود، آیا مشخصههای فرزند دارد یا نه، و نوع داده را مشخص میکند. |
متغیرهای ضبط جایگزین
همه عبارتهای قانون به گرهها اشاره میکنند. یک عبارت میتواند به یک گره خاص اشاره کند یا از متغیرهای ضبط عامنما $ برای اشاره به مجموعهای از گرهها در یک سطح از سلسله مراتب استفاده کند. از این متغیرهای ضبط برای ذخیره کردن مقدار کلیدهای گره استفاده کنید تا بتوانید از آنها در عبارتهای قانون بعدی استفاده کنید. این تکنیک به شما امکان میدهد
Security Rules شرایط پیچیدهتری بنویسید، چیزی که در موضوع بعدی با جزئیات بیشتر پوشش خواهیم داد.
{ "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 });
Objective-C
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 }];
Swift
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 }); });
REST (انتقال بازنمودی وضعیت)
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 });
Objective-C
FIRDatabaseReference *ref = [[FIRDatabase database] reference]; [[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) { // SUCCESS! }];
Swift
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 } });
REST (انتقال بازنمودی وضعیت)
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» عمیقتر کنید:
مفهوم اصلی بعدی زبان Security Rules، یعنی شرایط پویا را یاد بگیرید که به Security Rules شما اجازه میدهد مجوز کاربر را بررسی کند، دادههای موجود و ورودی را مقایسه کند، دادههای ورودی را اعتبارسنجی کند، ساختار پُرسمانهای دریافتی از کارخواه را بررسی کند، و غیره.
موارد استفاده امنیتی معمول و تعاریف «قوانین امنیتی Firebase» را که به آنها میپردازند مرور کنید.