نوشتن شرایط برای «قوانین امنیتی Cloud Firestore»

این راهنما براساس راهنمای ساختاردهی قوانین امنیتی ساخته شده است تا نشان دهد چگونه شرایط را به Cloud Firestore Security Rules خود اضافه کنید. اگر با اصول اولیه Cloud Firestore Security Rules آشنا نیستید، راهنمای شروع به کار را ببینید.

واحد سازنده اصلی Cloud Firestore Security Rules شرط است. شرط عبارت بولی است که تعیین می‌کند آیا عملیات خاصی باید مجاز یا رد شود. از قوانین امنیتی برای نوشتن شرایطی استفاده کنید که اصالت‌سنجی کاربر را بررسی می‌کند، داده‌های ورودی را اعتبارسنجی می‌کند، یا حتی به بخش‌های دیگر پایگاه داده‌تان دسترسی پیدا می‌کند.

برای کمک هوش مصنوعی، پیام‌واره زیر را در عامل کدنویسی هوش مصنوعی خود (مانند Gemini در Android Studio،‏ Antigravity،‏ Claude Code،‏ Codex، و Cursor) کپی کنید. می‌توانید پیام‌واره را سفارشی‌سازی کنید و با نماینده‌تان تکرار کنید. برای اطلاعات دقیق نصب، به مهارت‌های عامل Firebase مراجعه کنید.

Help me use Firebase in my app.
1. Make sure that **Firebase agent skills** are installed and updated. For Claude Code, Codex, or Cursor agents, help me install the Firebase plugin. For other agents, run `npx skills add firebase/agent-skills`. Always try to use **Firebase agent skills** for Firebase-related tasks.
2. Help me write, validate, and deploy Firebase Security Rules to protect my app's Firestore data.

اصالت‌سنجی

یکی از رایج‌ترین الگوهای قانون امنیتی، کنترل دسترسی براساس وضعیت اصالت‌سنجی کاربر است. برای مثال، ممکن است برنامه‌تان بخواهد فقط به کاربران واردشده به سیستم اجازه دهد داده بنویسند:

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to access documents in the "cities" collection
    // only if they are authenticated.
    match /cities/{city} {
      allow read, write: if request.auth != null;
    }
  }
}

الگوی رایج دیگر این است که مطمئن شوید کاربران فقط می‌توانند داده‌های خودشان را بخوانند و بنویسند:

service cloud.firestore {
  match /databases/{database}/documents {
    // Make sure the uid of the requesting user matches name of the user
    // document. The wildcard expression {userId} makes the userId variable
    // available in rules.
    match /users/{userId} {
      allow read, update, delete: if request.auth != null && request.auth.uid == userId;
      allow create: if request.auth != null;
    }
  }
}

اگر برنامه‌تان از «اصالت‌سنجی Firebase» یا Google Cloud Identity Platform استفاده می‌کند، متغیر request.auth حاوی اطلاعات اصالت‌سنجی برای کارخواه درخواست‌کننده داده است. برای اطلاعات بیشتر درباره request.auth، اسناد مرجع را ببینید.

اعتبارسنجی داده

بسیاری از برنامه‌ها اطلاعات کنترل دسترسی را به‌صورت فیلدهایی در اسناد پایگاه داده ذخیره می‌کنند. Cloud Firestore Security Rules می‌تواند به‌طور پویا دسترسی را براساس داده‌های سند مجاز یا رد کند:

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to read data if the document has the 'visibility'
    // field set to 'public'
    match /cities/{city} {
      allow read: if resource.data.visibility == 'public';
    }
  }
}

متغیر resource به سند درخواست‌شده اشاره دارد و resource.data نقشه‌ای از همه فیلدها و مقادیر ذخیره‌شده در سند است. برای اطلاعات بیشتر درباره متغیر resource، سند مرجع را ببینید.

هنگام نوشتن داده‌ها، ممکن است بخواهید داده‌های ورودی را با داده‌های موجود مقایسه کنید. در این حالت، اگر مجموعه قوانین شما نوشتن معلق را مجاز کند، متغیر request.resource حاوی وضعیت آینده سند است. برای عملیات update که فقط زیرمجموعه‌ای از فیلدهای سند را اصلاح می‌کنند، متغیر request.resource حاوی وضعیت سند معلق پس‌از عملیات خواهد بود. می‌توانید مقادیر فیلد را در request.resource بررسی کنید تا از به‌روزرسانی‌های ناخواسته یا ناسازگار داده‌ها جلوگیری کنید:

service cloud.firestore {
  match /databases/{database}/documents {
    // Make sure all cities have a positive population and
    // the name is not changed
    match /cities/{city} {
      allow update: if request.resource.data.population > 0
                    && request.resource.data.name == resource.data.name;
    }
  }
}

دسترسی به اسناد دیگر

بااستفاده از توابع get() و exists()، قوانین امنیتی شما می‌توانند درخواست‌های ورودی را درمقابل سایر اسناد موجود در پایگاه داده ارزیابی کنند. هر دو تابع get() و exists() انتظار دارند مسیرهای سند به‌طور کامل مشخص شده باشند. هنگام استفاده از متغیرها برای ساختن مسیرهای get() و exists()، باید صریحاً متغیرها را بااستفاده از نحو $(variable) گریز دهید.

در مثال زیر، متغیر database توسط عبارت مطابقت match /databases/{database}/documents ضبط می‌شود و برای تشکیل مسیر استفاده می‌شود:

service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city} {
      // Make sure a 'users' document exists for the requesting user before
      // allowing any writes to the 'cities' collection
      allow create: if request.auth != null && exists(/databases/$(database)/documents/users/$(request.auth.uid));

      // Allow the user to delete cities if their user document has the
      // 'admin' field set to 'true'
      allow delete: if request.auth != null && get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true;
    }
  }
}

برای نوشتن، می‌توانید از تابع getAfter() برای دسترسی به وضعیت سند پس‌از تکمیل تراکنش یا دسته نوشتن‌ها اما قبل‌از ثبت تراکنش یا دسته استفاده کنید. مانند get()، تابع getAfter() یک مسیر سند کاملاً مشخص‌شده را می‌گیرد. می‌توانید از getAfter() برای تعریف مجموعه‌های نوشتن که باید باهم به‌عنوان تراکنش یا دسته انجام شوند استفاده کنید.

دسترسی به حدود تماس

برای فراخوانی‌های دسترسی به سند در هر ارزیابی مجموعه قانون، محدودیتی وجود دارد:

  • ۱۰ برای درخواست‌های تک‌سندی و درخواست‌های پُرسمان.
  • ۲۰ برای خواندن چندسندی، تراکنش‌ها، و نوشتن دسته‌ای. محدودیت قبلی ۱۰ برای هر عملیات نیز اعمال می‌شود.

    برای مثال، فرض کنید یک درخواست نوشتن دسته‌ای با ۳ عملیات نوشتن ایجاد می‌کنید و قوانین امنیتی شما از ۲ فراخوانی دسترسی به سند برای اعتبارسنجی هر نوشتن استفاده می‌کنند. در این حالت، هر نوشتن از ۲ تماس دسترسی ۱۰تایی خود استفاده می‌کند و درخواست نوشتن دسته‌ای از ۶ تماس دسترسی ۲۰تایی خود استفاده می‌کند.

فراتر رفتن از هریک از این حدود منجر به خطای «اجازه رد شد» می‌شود. برخی‌از تماس‌های دسترسی به سند ممکن است ذخیره موقت شوند و تماس‌های ذخیره‌شده موقت در محدودیت‌ها محاسبه نمی‌شوند.

برای توضیح دقیق درباره نحوه تأثیر این محدودیت‌ها بر تراکنش‌ها و نوشتن‌های دسته‌ای، راهنمای ایمن‌سازی عملیات اتمی را ببینید.

دسترسی به تماس‌ها و قیمت‌گذاری

استفاده از این توابع یک عملیات خواندن را در پایگاه داده شما اجرا می‌کند، به این معنی که حتی اگر قوانین شما درخواست را رد کنند، برای خواندن اسناد صورت‌حساب دریافت خواهید کرد. برای اطلاعات صورت‌حساب خاص‌تر، قیمت‌گذاری قوانین را ببینید.

توابع سفارشی

As your security rules become more complex, you may want to wrap sets of conditions in functions that you can reuse across your ruleset. قوانین امنیتی از توابع سفارشی پشتیبانی می‌کنند. نحو توابع سفارشی کمی شبیه جاوا اسکریپت است، اما توابع قوانین امنیتی به زبان خاص دامنه نوشته شده‌اند که محدودیت‌های مهمی دارد:

  • توابع فقط می‌توانند حاوی یک عبارت return باشند. این فیلدها نباید حاوی منطق دیگری باشند. برای مثال، نمی‌توانند حلقه‌ها را اجرا کنند یا سرویس‌های خارجی را فراخوانی کنند.
  • توابع می‌توانند به‌طور خودکار به توابع و متغیرها از حوزه‌ای که در آن تعریف شده‌اند دسترسی داشته باشند. برای مثال، عملکردی که در حوزه service cloud.firestore تعریف شده است به متغیر resource و عملکردهای داخلی مانند get() و exists() دسترسی دارد.
  • توابع می‌توانند توابع دیگر را فراخوانی کنند اما نمی‌توانند بازگشتی باشند. عمق کل پشته فراخوانی به ۱۰ محدود است.
  • در نسخه v2 قوانین، توابع می‌توانند متغیرها را بااستفاده از کلیدواژه let تعریف کنند. توابع می‌توانند تا ۱۰ اتصال let داشته باشند، اما باید با دستور بازگشتی پایان یابند.

تابع با کلیدواژه 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();
    }
  }
}

استفاده از توابع در قوانین امنیتی باعث می‌شود با افزایش پیچیدگی قوانین، نگهداری آن‌ها آسان‌تر شود.

قوانین فیلتر نیستند

وقتی داده‌هایتان را ایمن کردید و شروع به نوشتن پُرسمان کردید، به‌خاطر داشته باشید که قوانین امنیتی فیلتر نیستند. نمی‌توانید برای همه اسناد موجود در یک مجموعه پُرسمان بنویسید و انتظار داشته باشید که «قوانین» فقط اسنادی را برگرداند که مشتری فعلی اجازه دسترسی به آن‌ها را دارد.

برای مثال، قانون امنیتی زیر را درنظر بگیرید:

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to read data if the document has the 'visibility'
    // field set to 'public'
    match /cities/{city} {
      allow read: if resource.data.visibility == 'public';
    }
  }
}

ردشده: این قانون پُرسمان زیر را رد می‌کند زیرا مجموعه نتایج می‌تواند شامل اسنادی باشد که در آن‌ها visibility public نیست:

وب
db.collection("cities").get()
    .then(function(querySnapshot) {
        querySnapshot.forEach(function(doc) {
            console.log(doc.id, " => ", doc.data());
    });
});

مجاز: این قانون پُرسمان زیر را مجاز می‌کند زیرا عبارت where("visibility", "==", "public") تضمین می‌کند که مجموعه نتایج شرط قانون را برآورده می‌کند:

وب
db.collection("cities").where("visibility", "==", "public").get()
    .then(function(querySnapshot) {
        querySnapshot.forEach(function(doc) {
            console.log(doc.id, " => ", doc.data());
        });
    });

قوانین امنیتی Rules هر پُرسمان را براساس نتیجه احتمالی آن ارزیابی می‌کند و اگر پُرسمان بتواند سندی را برگرداند که مشتری اجازه خواندن آن را ندارد، درخواست را رد می‌کند. پرس‌وجوها باید از محدودیت‌های تعیین‌شده توسط قوانین امنیتی شما پیروی کنند. برای اطلاعات بیشتر درباره قوانین و پُرسمان‌های امنیتی، به پُرسمان امن داده‌ها مراجعه کنید.

مراحل بعدی