چند سال پیش، در بازبینی امنیتی یک API فروشگاهی، به یافته‌ای برخوردم که تیم فنی‌اش آن را باور نمی‌کرد. کاربری وارد شده بود، توکن معتبر داشت، و از نظر لایه ورود، هیچ مشکلی نداشت. اما با تغییر یک عدد در URL، به سفارش‌های کاربر دیگری دسترسی پیدا می‌کرد. توضیح ساده بود: تیم، احراز هویت (Authentication) را ساخته بود، اما مجوزدهی (Authorization) را در سطح دسترسی به شیء (Object-level) پیاده نکرده بود. این یکی از رایج‌ترین سوءتفاهم‌ها در معماری سیستم‌هاست: فرض بر این است که اگر کاربر احراز هویت شد، بقیه لایه‌ها به‌طور خودکار امن است. در عمل، این دو لایه‌ای بنیادی متفاوتند که هر کدام شکست‌های خاص خودشان را دارند. این مقاله از دید کسی نوشته شده که سال‌ها روی سیستم‌های احراز هویت و کنترل دسترسی کار کرده و یاد گرفته که ابهام در مرز این دو، منشأ بیشتر آسیب‌پذیری‌های جدی است.

احراز هویت و مجوزدهی دقیقاً چه هستند؟

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

احراز هویت (Authentication یا AuthN) فرآیندی است که در آن، سیستم تأیید می‌کند کاربر واقعاً همان کسی است که ادعا می‌کند. این فرآیند به پرسش کیستی پاسخ می‌دهد: آیا این کاربر همان کسی است که می‌گوید هست؟ خروجی احراز هویت، تأیید هویت یک کاربر است — معمولاً به‌شکل یک نشست (Session) یا توکن (Token). انواع روش‌های احراز هویت در احراز هویت چیست و چه انواعی دارد آمده است.

مجوزدهی (Authorization یا AuthZ) فرآیندی است که در آن، سیستم تعیین می‌کند کاربر تأییدشده، به چه منابعی و با چه سطحی دسترسی دارد. این فرآیند به پرسش مجاز بودن پاسخ می‌دهد: آیا این کاربر مجاز است که این عملیات را روی این منبع انجام دهد؟ خروجی مجوزدهی، تصمیم باینری (اجازه یا رد) است که در هر درخواست اتخاذ می‌شود. روش‌های مجوزدهی و مدیریت نقش‌ها در بهترین روش‌های احراز هویت کاربران و بحث‌های مرتبط آمده است.

پس تفاوت بنیادی این است: احراز هویت یک بار اتفاق می‌افتد (در لحظه ورود)، مجوزدهی در هر درخواست تکرار می‌شود. احراز هویت هویت را تثبیت می‌کند، مجوزدهی دسترسی را کنترل می‌کند. این دو، دو لایه متفاوتند که اغلب در یک جریان یکپارچه، یکسان فرض می‌شوند.

احراز هویت، دروازه ورود است. مجوزدهی، دکمه‌های درون اتاق. عبور از دروازه، هیچ دکمه‌ای را برای شما باز نمی‌کند.

چارچوب AAA: زمینه تاریخی و مهندسی

برای درک عمیق این تفکیک، باید ریشه تاریخی آن را دانست. در ادبیات شبکه‌های قدیمی، سه‌گانه‌ای به‌نام AAA (Authentication, Authorization, Accounting) شکل گرفت که چارچوبی برای مدیریت دسترسی در شبکه‌های سازمانی فراهم می‌کرد:

  • Authentication (احراز هویت): تأیید هویت کاربر در زمان ورود.
  • Authorization (مجوزدهی): تعیین دسترسی کاربر تأییدشده به منابع مشخص.
  • Accounting (حسابرسی): ثبت و پایش فعالیت‌های کاربر پس از ورود، برای اهداف امنیتی و مالی.

این چارچوب، در پروتکل‌های شبکه‌ای مثل RADIUS و TACACS+ و بعدها در معماری‌های مدرن وب بازتاب یافت. نکته مهم این است که هر سه لایه، از هم مستقلند: ممکن است احراز هویت موفق باشد اما مجوزدهی رد کند؛ یا احراز هویت و مجوزدهی درست باشند اما حسابرسی به دلایل رعایتی (Compliance) مختل شود. در پروژه‌های سازمانی، مدیریت هم‌زمان این سه لایه، بخش ضروری معماری است.

لایه سوم که در ادبیات روزمره کمتر به آن پرداخته می‌شود، Accounting است. در معماری‌های مدرن، این لایه زیر عنوان Audit Logging یا Observability شناخته می‌شود. در بررسی لاگ‌های دیتابیس و بخش‌های مرتبط با پایش امنیتی، این لایه به‌طور عمیق‌تری بررسی شده است.

مدل مسئولیت: چه کسی پاسخ کدام پرسش است؟

جدول زیر، تفکیک مسئولیت‌ها را در یک نگاه نشان می‌دهد:

محوراحراز هویت (AuthN)مجوزدهی (AuthZ)
پرسش اصلیشما کی هستید؟چه کاری مجاز هستید؟
زمان اجرادر لحظه وروددر هر درخواست
خروجینشست یا توکنتصمیم اجازه یا رد
مخاطب تصمیمخود کاربرمنبع یا عملیات
مدل دادهکاربر و Credentialنقش، مجوز، منبع
شکستورود ناموفق، نشست نامعتبردسترسی رد شده، افشای داده
پروتکل‌های مرتبطSAML، OIDC، KerberosOAuth2 (Scope)، XACML، Cedar
پوشش در OWASPA07: Identification and Authentication FailuresA01: Broken Access Control

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

احراز هویت: از رمز عبور تا MFA

احراز هویت، بر پایه سه دسته از فاکتورها ساخته می‌شود:

  • چیزی که می‌دانید (Something you know): رمز عبور، PIN، پاسخ پرسش امنیتی.
  • چیزی که دارید (Something you have): تلفن همراه، سخت‌افزار توکن، ایمیل.
  • چیزی که هستید (Something you are): اثر انگشت، تشخیص چهره، الگوی شبکیه.

احراز هویت تک‌فاکتوری، فقط از یک دسته استفاده می‌کند. احراز هویت چندفاکتوری (Multi-Factor Authentication یا MFA)، از دو یا چند دسته متفاوت. نکته مهم فنی: ترکیب دو فاکتور از یک دسته (مثلاً رمز عبور و پرسش امنیتی) MFA محسوب نمی‌شود، چون هر دو از جنس چیزی هستند که می‌دانید. اثر این تفکیک در امنیت واقعی، قابل توجه است. راهنمای اجرای 2FA برای وردپرس در فعال‌سازی 2FA برای کاربران وردپرس و تحلیل اثر آن در احراز هویت دو مرحله‌ای چگونه امنیت را افزایش می‌دهد آمده است.

در سطح پروتکل، چند استاندارد اصلی وجود دارد:

  • SAML 2.0: استاندارد قدیمی‌تر که در سازمان‌ها رایج است، بر پایه XML.
  • OpenID Connect (OIDC): استاندارد مدرن لایه احراز هویت که روی OAuth2 ساخته شده، بر پایه JSON و JWT.
  • Kerberos: استاندارد سنتی سازمانی برای Single Sign-On، مبتنی بر Ticket.
  • WebAuthn: استاندارد مدرن که احراز هویت بدون رمز عبور را با کلید عمومی و امضای دیجیتال ممکن می‌کند.

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

مجوزدهی: از ACL تا RBAC و ABAC و ReBAC

مجوزدهی، بر پایه چند مدل شناخته‌شده ساخته می‌شود. انتخاب مدل درست، به پیچیدگی نیازهای دسترسی سازمان بستگی دارد:

  • Access Control List (ACL): ساده‌ترین مدل. برای هر منبع، فهرستی از کاربران مجاز نگه داشته می‌شود. مناسب سیستم‌های کوچک با تعداد کاربران و منابع محدود.
  • Role-Based Access Control (RBAC): دسترسی‌ها بر پایه نقش تعریف می‌شوند. کاربر، نقش می‌گیرد و از طریق نقش، به دسترسی‌ها می‌رسد. مدل غالب در اکثر سیستم‌های تجاری.
  • Attribute-Based Access Control (ABAC): دسترسی‌ها بر پایه ترکیب ویژگی‌های کاربر، منبع، عملیات و بستر. انعطاف‌پذیری بالا، پیچیدگی پیاده‌سازی بیشتر. مناسب سیستم‌های با نیازهای سیاستی پیچیده.
  • Relationship-Based Access Control (ReBAC): دسترسی‌ها بر پایه روابط بین کاربر و منبع. مثلاً در گوگل درایو، دسترسی به یک فایل بر پایه این است که شما با مالک فایل چه رابطه‌ای دارید (مالک، ویرایشگر، بیننده). سیستم‌هایی مثل Google Zanzibar بر پایه این مدل ساخته شده‌اند.
  • Mandatory Access Control (MAC): دسترسی‌ها بر پایه برچسب‌های امنیتی سخت‌گیرانه. در سیستم‌های نظامی و دولتی رایج است.
  • Discretionary Access Control (DAC): مالک منبع، تعیین می‌کند چه کسی دسترسی دارد. مدل رایج در سیستم‌های فایل لینوکس.

انتخاب بین این مدل‌ها، معمولاً به ترکیب سه فاکتور بستگی دارد: تعداد کاربران، پیچیدگی روابط و نیاز به انعطاف‌پذیری سیاستی. در پروژه‌های واقعی، مدل ترکیبی هم رایج است: مثلاً RBAC برای دسترسی‌های عمومی و ABAC برای سناریوهای حساس. راهنمای عملی بررسی دسترسی‌ها در مدیریت کاربران و دسترسی‌های دیتابیس و بخش‌های مرتبط آمده است.

معماری لایه‌ای در سیستم‌های وب

در سیستم‌های وب مدرن، احراز هویت و مجوزدهی در یک معماری سه‌لایه پیاده‌سازی می‌شوند:

  1. لایه Presentation (Frontend): ورود کاربر، ارسال Credential، نگهداری نشست یا توکن، نمایش یا پنهان‌کردن عناصر بر اساس دسترسی.
  2. لایه Application (Backend): اعتبارسنجی Credential، صدور نشست یا توکن، بررسی مجوز در هر درخواست، اجرای منطق کسب‌وکار.
  3. لایه Data (Database): ذخیره کاربران، نقش‌ها، مجوزها و لاگ حسابرسی.

خطای رایج در پروژه‌ها: تمرکز بر لایه Frontend برای پنهان‌کردن عناصر، بدون پیاده‌سازی هم‌ارز در لایه Backend. این الگو، به آسیب‌پذیری‌ای منتهی می‌شود که در ادبیات با عنوان Broken Access Control (کنترل دسترسی شکسته) شناخته می‌شود. کاربر می‌تواند با فراخوانی مستقیم API، از کنترل‌های Frontend عبور کند. اصل بنیادی: هر تصمیم امنیتی، باید در لایه Backend نیز پیاده‌سازی شود، مستقل از آن‌چه Frontend انجام می‌دهد.

معماری صحیح، سه ویژگی دارد:

  • Defense in Depth (دفاع لایه‌ای): کنترل‌ها در چند لایه اعمال می‌شوند، تا شکست یک لایه، به شکست کل سیستم منتهی نشود.
  • Separation of Concerns (تفکیک دغدغه‌ها): لایه احراز هویت، مستقل از لایه مجوزدهی پیاده‌سازی می‌شود. هر کدام می‌تواند بدون تأثیر بر دیگری تغییر کند.
  • Fail-Safe Defaults (پیش‌فرض امن): در نبود تصمیم صریح، دسترسی رد می‌شود، نه اجازه. قاعده Deny by Default، یکی از اصول پایه امنیت سیستم‌هاست.

OAuth2 و OpenID Connect: نقطه ابهام تاریخی

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

مثال متعارف: کاربر به یک اپلیکیشن شخص ثالث اجازه می‌دهد که به Google Calendar او دسترسی داشته باشد. اپلیکیشن ثالث، یک Access Token می‌گیرد که دسترسی محدود به تقویم را در بر دارد. در این جریان، احراز هویت کاربر، روی Google انجام می‌شود، اما اطلاعات احراز هویت به اپلیکیشن ثالث نمی‌رسد. این، مسئله‌ای که OAuth2 حل می‌کند، از جنس مجوزدهی است.

با این حال، در اوایل، توسعه‌دهندگان از OAuth2 برای احراز هویت هم استفاده می‌کردند، که منجر به آسیب‌پذیری‌های جدی شد. برای رفع این مسئله، استاندارد OpenID Connect (OIDC) در بالای OAuth2 ساخته شد که لایه احراز هویت را به‌طور مشخص تعریف می‌کند. در OIDC، علاوه بر Access Token (برای مجوزدهی)، یک ID Token هم صادر می‌شود که اطلاعات هویتی کاربر را حمل می‌کند.

پیامد عملی: در پروژه‌ای که ورود با Google یا GitHub را پیاده می‌کنید، باید دقت کنید که OAuth2 تنها برای احراز هویت کافی نیست. برای احراز هویت مطمئن، باید از OIDC استفاده کنید و ID Token را اعتبارسنجی کنید. مسیر کامل این جریان در OAuth چیست و چگونه کار می‌کند آمده است.

OAuth2، پروتکل مجوزدهی است. OpenID Connect، لایه احراز هویت روی آن. اگر جریان ورود شما فقط OAuth2 است، احتمالاً در حال استفاده از یک پروتکل برای کاری هستید که برای آن طراحی نشده.

JWT و نقش دوگانه در احراز هویت و مجوزدهی

JWT (JSON Web Token) یکی از پرکاربردترین ساختارهای توکن در سیستم‌های مدرن است. JWT، در باطن یک ساختار سه‌بخشی (Header، Payload، Signature) است که اطلاعات را به‌صورت امضاشده حمل می‌کند. این ساختار، به‌طور طبیعی در هر دو لایه احراز هویت و مجوزدهی ظاهر می‌شود:

  • در احراز هویت: JWT می‌تواند به‌عنوان توکن هویت استفاده شود. در OIDC، ID Token یک JWT است که اطلاعات کاربر را در Claimهای مشخص (sub، email، name) حمل می‌کند.
  • در مجوزدهی: JWT می‌تواند اطلاعات مجوز (نقش‌ها، Scopeها، سطح دسترسی) را در Claimهای خود داشته باشد. سرور، در هر درخواست، این Claimها را اعتبارسنجی و بر اساس آن‌ها تصمیم می‌گیرد.

این نقش دوگانه، منشأ سوءاستفاده‌های رایج است. برخی توسعه‌دهندگان، همه اطلاعات مجوز را در JWT می‌گذارند و فرض می‌کنند که این کافی است. اما JWT، به‌طور پیش‌فرض فقط صحت خودش را اثبات می‌کند؛ به‌معنای این نیست که کاربر، همچنان مجاز به همان عملیات است. سه مسئله فنی مهم:

  1. انقضای توکن (Expiration): اگر JWT طول عمر بلند داشته باشد، لغو دسترسی کاربر قبل از انقضا ناممکن است. برای حل این مسئله، الگوهای Access Token کوتاه‌عمر + Refresh Token رایج است.
  2. عدم امکان ابطال فوری (Revocation): JWT ذاتاً Stateless است. اگر بخواهید یک JWT را فوراً ابطال کنید، به یک لایه ذخیره‌سازی (مثل Deny List) نیاز دارید که همین Stateless بودن را نقض می‌کند.
  3. افشای اطلاعات: Payload در JWT، به‌صورت Base64 کدگذاری می‌شود، نه رمزنگاری. یعنی هر کسی می‌تواند محتوای JWT را بخواند. این یعنی نباید اطلاعات حساس در Payload قرار گیرد.

مرور کامل JWT و کاربردهایش در JWT چیست و چه کاربردی در احراز هویت دارد آمده است. برای سناریوهای سازمانی با نیاز به SSO، ترکیب OIDC و JWT در راه‌اندازی SSO برای تیم‌های دورکار بررسی شده است.

حملات رایج در هر لایه

هر لایه، شکست‌های خاص خودش را دارد. شناخت این شکست‌ها، در طراحی کنترل‌های امنیتی، حیاتی است:

حملات در لایه احراز هویت

  • Brute Force (حمله جستجوی فراگیر): تلاش مکرر برای حدس رمز عبور. راه‌حل: Rate Limiting، محدودسازی تلاش ناموفق، CAPTCHA.
  • Credential Stuffing (پرکردن اطلاعات ورود): استفاده از جفت کاربری-رمز لو رفته از سایت‌های دیگر. راه‌حل: 2FA، بررسی هویت جامع، پایش رفتار کاربر.
  • Session Fixation (تثبیت نشست): مهاجم یک Session ID را از قبل تثبیت می‌کند و کاربر را به استفاده از آن هدایت می‌کند. راه‌حل: چرخش Session ID پس از ورود موفق.
  • Session Hijacking (ربایش نشست): دزدیدن Session ID فعال. راه‌حل: کوکی‌های Secure و HttpOnly، SameSite، HSTS.
  • MFA Bypass (دور زدن MFA): دور زدن احراز هویت چندفاکتوری از طریق API مستقیم یا نقص پیاده‌سازی. راه‌حل: اعتبارسنجی MFA در سطح منطق کسب‌وکار، نه فقط Frontend.

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

حملات در لایه مجوزدهی

  • IDOR (Insecure Direct Object Reference): دسترسی به شیء بدون بررسی مجوز. مهاجم با تغییر پارامتر در URL یا API، به منبعی که نباید دسترسی پیدا می‌کند. راه‌حل: بررسی مجوز در سطح شیء در هر درخواست. مرور کامل در آسیب‌پذیری IDOR چیست و چگونه رفع می‌شود.
  • Privilege Escalation (ارتقای دسترسی): Vertical (از نقش پایین به بالا) یا Horizontal (از کاربری به کاربر هم‌سطح). راه‌حل: کنترل سخت‌گیرانه نقش و دسترسی، جداسازی وظایف.
  • Mass Assignment (تخصیص انبوه): ارسال فیلدهای اضافی در درخواست که فریم‌ورک به‌طور خودکار روی مدل اعمال می‌کند. راه‌حل: Whitelisting فیلدهای قابل‌قبول.
  • Broken Function Level Authorization (مجوزدهی معیوب در سطح تابع): نبود کنترل دسترسی روی Endpointهای مدیریتی. راه‌حل: اعتبارسنجی نقش در تمامی Endpointها، بدون استثنا.
  • Path Traversal (پیمایش مسیر): دسترسی به فایل‌های خارج از محدوده مجاز از طریق دست‌کاری مسیر. راه‌حل: محدودسازی مسیرهای مجاز، بررسی ورودی.

لایه مجوزدهی، یکی از پرتنش‌ترین لایه‌های امنیتی است، چون در هر درخواست تکرار می‌شود و امکان خطای انسانی در آن بالاست. مرور کلی این کلاس‌ها در انواع آسیب‌پذیری‌های رایج وب آمده است.

جایگاه در OWASP Top 10

فهرست OWASP Top 10، مرجع استاندارد آسیب‌پذیری‌های وب است. در نسخه‌های اخیر، هر دو لایه در فهرست حضور دارند:

  • A01: Broken Access Control: اختصاص به لایه مجوزدهی دارد. این دسته، در نسخه‌های اخیر به رتبه اول رسیده، چون نه اسکنرهای خودکار به‌خوبی آن را می‌بینند و نه توسعه‌دهندگان، معمولاً آن را در تمرکز قرار می‌دهند.
  • A07: Identification and Authentication Failures: اختصاص به لایه احراز هویت دارد. شامل ضعف در رمز عبور، فقدان MFA، مدیریت نادرست نشست.

این تفکیک در OWASP، به‌خوبی نشان می‌دهد که حتی در ادبیات استاندارد، این دو لایه به‌طور جداگانه بررسی می‌شوند. اگر با روش تست این آسیب‌پذیری‌ها آشنا نیستید، تست امنیت وب‌سایت چگونه انجام می‌شود و تست امنیت وب در سطح پروتکل دو زاویه مکمل را ارائه می‌دهند.

احراز هویت و مجوزدهی در وردپرس

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

لایه احراز هویت در وردپرس

  • ذخیره‌سازی کاربران: در جدول wp_users و متادیتا در wp_usermeta.
  • هش رمز عبور: از الگوریتم phpHash با کتابخانه‌های مدرن مثل bcrypt. رمز عبور، هرگز به‌صورت Plain ذخیره نمی‌شود.
  • فرآیند ورود: تابع wp_signon()، هوک‌های wp_authenticate، و اعتبارسنجی کوکی‌های wordpress_logged_in_*.
  • نشست: کوکی‌های HttpOnly، Secure (در HTTPS)، SameSite.

لایه مجوزدهی در وردپرس

  • نقش‌ها (Roles): Administrator، Editor، Author، Contributor، Subscriber — سیستم RBAC پایه.
  • قابلیت‌ها (Capabilities): هر نقش، مجموعه‌ای از Capability دارد مثل edit_posts، manage_options، publish_posts.
  • توابع بررسی: current_user_can()، user_can()، author_can() — ابزار اصلی بررسی مجوز در هر درخواست.
  • غیرس (Nonce): توکن‌هایی برای محافظت از فرم‌ها و درخواست‌های حساس در برابر CSRF (Cross-Site Request Forgery).
  • REST API: هر Endpoint، پارامتر permission_callback دارد که تصمیم مجوز را کنترل می‌کند.

نکته فنی مهم در وردپرس: تفکیک لایه احراز هویت از مجوزدهی در سطح فریم‌ورک، یکی از نقاط قوت آن است. اما در افزونه‌های شخص ثالث، این تفکیک به‌طور مرتب نقض می‌شود. برخی افزونه‌ها، فقط ورود کاربر را بررسی می‌کنند (احراز هویت) و فرض می‌کنند که کاربر وارد شده، به همه کارها مجاز است (نادیده‌گرفتن مجوزدهی). این الگو، منشأ بیشتر آسیب‌پذیری‌های IDOR در افزونه‌های وردپرسی است. راهنمای تشخیص این کلاس آسیب‌پذیری در آسیب‌پذیری افزونه‌های وردپرس چه خطراتی دارد آمده است.

برای محافظت بیشتر در وردپرس، توصیه‌های عملی در امن‌سازی ورود ادمین وردپرس و احراز هویت در وردپرس چگونه تقویت می‌شود جمع‌آوری شده است.

پیاده‌سازی در API و SPA

در معماری‌های مدرن، به‌خصوص در Single Page Applicationها (SPA) و APIهای REST و GraphQL، تفکیک احراز هویت و مجوزدهی اهمیت ویژه‌ای دارد:

  • احراز هویت: در SPA، معمولاً با JWT یا OIDC انجام می‌شود. توکن در LocalStorage یا HttpOnly Cookie نگهداری می‌شود. هر بار که SPA به API درخواست می‌فرستد، توکن را در Authorization Header همراه می‌کند.
  • مجوزدهی: در سطح هر Endpoint، سرور تصمیم می‌گیرد که آیا کاربر مجاز به انجام این عملیات روی این منبع است. این تصمیم، بر پایه Claimهای توکن، نقش کاربر و وضعیت منبع گرفته می‌شود.

سه الگوی رایج پیاده‌سازی مجوزدهی در API:

  1. Middleware-based: یک لایه Middleware قبل از اجرای منطق Endpoint، بررسی مجوز را انجام می‌دهد. ساده، اما ممکن است تصمیم‌های سطح شیء را از دست بدهد.
  2. Policy-based: برای هر منبع، یک Policy تعریف می‌شود که شرایط دسترسی را مشخص می‌کند. در فریم‌ورک‌هایی مثل Laravel، این الگو رایج است.
  3. Service-level: بررسی مجوز، درون منطق کسب‌وکار انجام می‌شود، نه در لایه Edge. انعطاف‌پذیری بالاتر، پیچیدگی بیشتر.

توصیه عملی من: ترکیب Middleware-based برای تصمیم‌های عمومی، و Service-level برای تصمیم‌های سطح شیء. هر Endpoint، باید هم در لایه Edge و هم در لایه منطق، کنترل دسترسی داشته باشد. راهنمای کامل امنیت API در امنیت API در وب چگونه تامین می‌شود آمده است.

تست و اعتبارسنجی هر لایه

تست امنیتی این دو لایه، رویکردهای متفاوتی می‌طلبد:

تست احراز هویت

  • Brute-force مقاومت: تلاش برای چندین رمز عبور در بازه کوتاه. اگر Rate Limiting وجود دارد، سیستم باید پس از چند تلاش ناموفق، مسدود کند.
  • Password Policy: بررسی اجبار به رمز عبور با آنتروپی کافی. سیاست‌های قوی و ضعیف در مدیریت رمز عبور امن آمده است.
  • Session Management: بررسی چرخش Session ID پس از ورود، انقضای نشست غیرفعال، پرچم‌های کوکی.
  • 2FA Bypass: تلاش برای دور زدن 2FA از طریق API مستقیم.
  • Account Enumeration: بررسی یکسان بودن پاسخ سیستم برای نام کاربری موجود و غیرموجود.

تست مجوزدهی

  • IDOR Testing: تغییر پارامترهای شیء در URL و API، بررسی پاسخ سیستم.
  • Vertical Privilege Escalation: تلاش یک کاربر سطح پایین برای دسترسی به Endpointهای سطح بالا.
  • Horizontal Privilege Escalation: تلاش برای دسترسی به داده کاربری دیگر در همان سطح.
  • Force Browsing: دسترسی مستقیم به URLهایی که در منو نمایش داده نمی‌شوند.
  • Mass Assignment: ارسال فیلدهای اضافی در JSON Body، بررسی اعمال آن‌ها روی مدل.

ابزارهای تست امنیتی، معمولاً در کشف لایه احراز هویت مؤثرترند تا لایه مجوزدهی. کشف IDOR و Privilege Escalation نیازمند تحلیل دستی است، چون رفتار سیستم در پاسخ به درخواست‌های متفاوت، با اسکنرهای خودکار قابل‌تشخیص کامل نیست. رویکرد سیستماتیک در چگونه آسیب‌پذیری سایت را پیدا کنیم آمده است.

اشتباهات رایج معماری

این اشتباهات را در پروژه‌های مختلف، زیاد دیده‌ام:

  • فرض کردن امنیت پس از احراز هویت: باور به این‌که اگر کاربر وارد شد، به‌طور خودکار مجاز به همه کارهاست. این باور، منشأ بیشتر IDORها است.
  • مجوزدهی فقط در لایه Frontend: پنهان کردن دکمه‌های UI بدون پیاده‌سازی مجوزدهی در Backend. کاربر می‌تواند با فراخوانی مستقیم API، از این کنترل عبور کند.
  • استفاده از احراز هویت به‌جای مجوزدهی: مثلاً بررسی این‌که «کاربر وارد شده است» به‌جای این‌که «آیا کاربر مجاز به این عملیات است». الگوی رایج در افزونه‌های وردپرسی.
  • نادیده گرفتن Object-level: پیاده‌سازی مجوزدهی در سطح Endpoint، اما فراموش کردن سطح شیء. مهاجم با تغییر پارامتر ID، به منبعی که نباید دسترسی پیدا می‌کند.
  • Putting All Trust in JWT: فرض کردن این‌که توکن معتبر، به‌خودی‌خود مجوز است. توکن، فقط احراز هویت را تأیید می‌کند؛ تصمیم مجوزدهی، باید در سرور گرفته شود.
  • مخلوط کردن منطق AuthN و AuthZ در یک ماژول: پیاده‌سازی هر دو لایه در یک ماژول واحد، کاهش انعطاف‌پذیری و افزایش ریسک. تفکیک دغدغه‌ها، یکی از اصول پایه معماری است.
  • عدم پیاده‌سازی Deny by Default: در نبود تصمیم صریح، سیستم اجازه می‌دهد. اصل درست، رد کردن پیش‌فرض است.
  • نبود Audit Logging: بدون ثبت لاگ، تشخیص نفوذ و پاسخ به حادثه، بسیار سخت می‌شود. لایه حسابرسی، بخشی از AAA است که غالباً نادیده گرفته می‌شود.
در امنیت سیستم، تفکیک احراز هویت از مجوزدهی، یک تصمیم معماری است، نه یک سبک کدنویسی. سیستمی که این دو را یکی می‌بیند، در اولین حمله جدی، نشان می‌دهد که درک درستی از لایه‌ها نداشته است.

مطالعه‌ای از یک رخنه واقعی

پروژه‌ای را به یاد می‌آورم که در آن، یک پلتفرم مدیریت فایل سازمانی، هدف یک رخنه واقعی شد. تیم فنی، سیستم احراز هویت قوی داشت: رمز عبور با bcrypt، 2FA اجباری، Rate Limiting، Session چرخشی. از منظر لایه احراز هویت، سیستم بی‌نقص بود.

اما رخنه از لایه مجوزدهی آمد. یکی از کاربران، با تغییر شناسه یک فایل در URL از /files/1234/download به /files/1235/download، به فایل کاربر دیگری دسترسی پیدا کرد. سیستم، فقط بررسی کرده بود که کاربر وارد شده است (احراز هویت)، اما بررسی نکرده بود که آیا کاربر مالک فایل است یا مجاز به دسترسی (مجوزدهی سطح شیء).

پیامد این رخنه، سه درس داشت:

  1. احراز هویت قوی، جبران‌کننده مجوزدهی ضعیف نیست: هر لایه، بایستی مستقل و کامل پیاده‌سازی شود.
  2. سطح شیء، جدی‌ترین لایه مجوزدهی است: Endpointهایی که به یک شیء مشخص دسترسی می‌دهند، باید همیشه بررسی مجوز در سطح شیء داشته باشند.
  3. تست امنیتی باید دو لایه را مستقل بسنجد: تست فقط احراز هویت، شکاف‌های مجوزدهی را نشان نمی‌دهد.

درمان این رخنه، ساده بود: افزودن یک بررسی مجوز در سطح شیء در تمام Endpointهای دانلود، و افزودن یک لایه تست خودکار که هر PR را در برابر سناریوهای IDOR چک کند. این اصل، در تمام Endpointهای آینده، به‌عنوان یک سیاست کدنویسی پذیرفته شد. مرور آسیب‌پذیری IDOR و راه‌حل‌هایش در آسیب‌پذیری IDOR چیست آمده است.

پرسش‌های پرتکرار درباره تفاوت احراز هویت و مجوزدهی

پرسش‌هایی که در جلسات مشاوره و آموزش زیاد می‌شنوم:

تفاوت احراز هویت و مجوزدهی در یک جمله چیست؟

احراز هویت پاسخ به پرسش کیستی است؛ مجوزدهی پاسخ به پرسش مجاز بودن. اولی در لحظه ورود اتفاق می‌افتد، دومی در هر درخواست تکرار می‌شود. اولی هویت را تثبیت می‌کند، دومی دسترسی را کنترل می‌کند.

آیا می‌توان مجوزدهی را در JWT انجام داد؟

تا حدی، بله. JWT می‌تواند Claimهای مجوز (نقش، Scope) را حمل کند. اما تصمیم نهایی مجوزدهی باید در سرور گرفته شود، چون JWT فقط صحت خودش را اثبات می‌کند، نه مجاز بودن عملیات جاری. علاوه بر این، تصمیم‌های سطح شیء را نمی‌توان در JWT قرار داد.

آیا OAuth2 برای احراز هویت کافی است؟

خیر. OAuth2 در باطن یک پروتکل مجوزدهی است، نه احراز هویت. اگر از OAuth2 برای ورود استفاده می‌کنید، باید لایه احراز هویت را با OpenID Connect (OIDC) تقویت کنید. ID Token در OIDC، برای احراز هویت است.

چرا مجوزدهی در فهرست OWASP Top 10 رتبه اول است؟

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

آیا پیاده‌سازی RBAC کافی است؟

نه همیشه. RBAC برای سناریوهای عمومی خوب است، اما در سناریوهای پیچیده‌تر (مثل دسترسی به فایل‌های اشتراکی یا سناریوهای مبتنی بر رابطه)، به ABAC یا ReBAC نیاز دارید. انتخاب مدل، به پیچیدگی نیازهای سازمان بستگی دارد.

در وردپرس، نقش‌ها و Capability‌ها چه رابطه‌ای دارند؟

نقش، مجموعه‌ای از Capabilityها است. مثلاً نقش Editor دارای Capabilityهایی مثل edit_others_posts، publish_posts است. کاربر، به یک نقش تخصیص داده می‌شود و از طریق آن نقش، به Capabilityها دسترسی دارد. تابع current_user_can()، برای بررسی Capability در زمان اجرا استفاده می‌شود. راهنمای کامل در احراز هویت در وردپرس و تنظیمات کاربران و نقش‌ها در وردپرس آمده است.

آیا Session و JWT دو رویکرد متفاوت به احراز هویت هستند؟

بله، از نظر معماری. Session رویکرد Stateful است: وضعیت نشست روی سرور نگهداری می‌شود. JWT رویکرد Stateless است: اطلاعات در خود توکن حمل می‌شود. هر کدام مزایا و معایب خودش را دارد. تفاوت‌های دقیق‌تر در JWT چیست آمده است.

چطور می‌توانم در پروژه‌ام این دو لایه را درست تفکیک کنم؟

سه اقدام عملی: اول، لایه‌ها را در معماری کد به‌صورت مستقل پیاده‌سازی کنید — ماژول احراز هویت، ماژول مجوزدهی. دوم، بررسی مجوز را در سطح Endpoint و سطح شیء به‌طور همزمان پیاده‌سازی کنید. سوم، تست‌های امنیتی را به‌طور مستقل برای هر لایه تعریف کنید.

آیا مجوزدهی در سمت Frontend بی‌فایده است؟

نه، مفید است، اما کافی نیست. مجوزدهی در Frontend، برای بهبود تجربه کاربری است: پنهان کردن دکمه‌هایی که کاربر به آن‌ها دسترسی ندارد. اما کنترل امنیتی واقعی، باید در Backend پیاده‌سازی شود، چون کاربر مهاجم می‌تواند با فراخوانی مستقیم API، از کنترل‌های Frontend عبور کند.

آیا از MFA به‌عنوان لایه مجوزدهی استفاده می‌شود؟

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

آیا احراز هویت و مجوزدهی می‌توانند توسط یک سرویس واحد انجام شوند؟

در عمل، معمولاً یک سرویس ارائه‌دهنده (Identity Provider) هر دو لایه را فراهم می‌کند، اما با تفکیک منطقی داخلی. در سیستم‌های سازمانی، معمولاً IdP لایه احراز هویت را انجام می‌دهد و سیستم‌های اپلیکیشن، لایه مجوزدهی را. تفکیک منطقی، در هر سناریو لازم است.

آیا ورود با Google یا GitHub امن است؟

بله، اگر درست پیاده‌سازی شود. امنیت آن، به دو شرط بستگی دارد: اول، استفاده از OpenID Connect با اعتبارسنجی کامل ID Token. دوم، پیاده‌سازی مجوزدهی داخلی مستقل از اطلاعات هویتی دریافت‌شده. توصیه‌های عملی در OAuth چیست آمده است.

تصویر نهایی در یک جمله

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

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

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