پرش به محتوای اصلی
آسیب‌پذیری‌های سایت

Mass Assignment چیست؟ بررسی آسیب‌پذیری تخصیص انبوه در API و وب‌اپلیکیشن

آسیب‌پذیری Mass Assignment یا تخصیص انبوه زمانی رخ می‌دهد که API یا وب‌اپلیکیشن بدون محدودسازی مناسب، داده‌های کاربر را مستقیماً به Propertyهای Object داخلی متصل کند. این ضعف می‌تواند باعث تغییر غیرمجاز اطلاعات حساسی مانند نقش کاربر، مالکیت، وضعیت تأیید، اطلاعات مالی یا Tenant شود. استفاده از DTO، Allowlist، Mapping صریح، Property-Level Authorization و Schema Validation از مهم‌ترین روش‌های پیشگیری از آسیب‌پذیری Mass Assignment هستند.

تیم امنیت رخنه‌کاو ۳۸ دقیقه مطالعه انتشار:

Mass Assignment یا تخصیص انبوه یک آسیب‌پذیری امنیتی است که زمانی رخ می‌دهد که وب‌اپلیکیشن یا API داده‌های دریافتی از کاربر را بدون محدودسازی دقیق، مستقیماً به ویژگی‌های یک Object یا Model داخلی متصل کند. در این شرایط ممکن است کاربر بتواند فیلدهایی مانند نقش حساب، وضعیت تأیید، موجودی، مالکیت یا سایر خصوصیات داخلی را تغییر دهد؛ درحالی‌که این فیلدها هرگز نباید تحت کنترل مستقیم او باشند.

آسیب‌پذیری Mass Assignment در نگاه اول ممکن است یک مشکل ساده در اعتبارسنجی ورودی به نظر برسد، اما در بسیاری از برنامه‌های مدرن مسئله اصلی عمیق‌تر است: سرور مرز مشخصی میان «داده‌ای که کاربر اجازه ارسال آن را دارد» و «داده‌ای که مدل داخلی برنامه قادر به ذخیره آن است» ایجاد نکرده است.

این مشکل به‌خصوص در APIهای REST، برنامه‌هایی که از ORM استفاده می‌کنند، سیستم‌های مبتنی بر Model Binding و پروژه‌هایی که داده JSON را مستقیماً به مدل‌های پایگاه داده منتقل می‌کنند اهمیت زیادی دارد.

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

به همین دلیل پیشگیری از آسیب‌پذیری Mass Assignment فقط به بررسی نوع داده یا فیلترکردن چند نام فیلد محدود نمی‌شود. طراحی امن باید مشخص کند هر Endpoint دقیقاً کدام Property را، برای کدام کاربر، تحت چه شرایطی و در چه مرحله‌ای از منطق کسب‌وکار اجازه تغییر می‌دهد.

Mass Assignment چیست؟

Mass Assignment که می‌توان آن را «تخصیص انبوه»، «انتساب انبوه» یا در بعضی فریم‌ورک‌ها نوعی Auto Binding دانست، زمانی اتفاق می‌افتد که Framework یا کد برنامه مجموعه‌ای از داده‌های ورودی را به‌صورت خودکار به Propertyهای یک Object متصل کند.

این قابلیت ذاتاً آسیب‌پذیری نیست.

درواقع Auto Binding برای توسعه‌دهندگان بسیار کاربردی است، زیرا به‌جای نوشتن کد جداگانه برای انتقال تک‌تک پارامترهای Request به یک Object، Framework می‌تواند این کار را به‌صورت خودکار انجام دهد.

فرض کنید درخواست ویرایش پروفایل شامل این اطلاعات باشد:

{
  "displayName": "Ali",
  "timezone": "Asia/Tehran",
  "language": "fa"
}

برنامه می‌تواند این داده‌ها را دریافت کرده و مقادیر مربوطه را روی شیء پروفایل کاربر قرار دهد.

تا اینجا مشکلی وجود ندارد.

مسئله از جایی شروع می‌شود که همان Object در سمت سرور Propertyهای دیگری نیز داشته باشد که نباید از طریق این Endpoint قابل تغییر باشند؛ برای مثال:

role
isAdmin
accountStatus
balance
emailVerified
ownerId
tenantId
approved
createdAt

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

بنابراین اصل مشکل در Mass Assignment این نیست که «چند مقدار هم‌زمان به Object اختصاص داده می‌شود»؛ مشکل این است که کنترل مشخصی بر Propertyهای قابل تغییر توسط Client وجود ندارد.

چرا آسیب‌پذیری Mass Assignment ایجاد می‌شود؟

در بسیاری از Frameworkهای مدرن، توسعه‌دهنده می‌تواند یک Request را با چند خط کد به Model تبدیل کند. این قابلیت سرعت توسعه را افزایش می‌دهد، اما اگر Domain Model، Database Model و Input Model همگی یک ساختار مشترک داشته باشند، سطح حمله نیز افزایش پیدا می‌کند.

یک معماری ساده ممکن است چنین مسیری داشته باشد:

Client
  ↓
HTTP Request
  ↓
Controller
  ↓
Automatic Binding
  ↓
Database Model
  ↓
Database

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

از دید امنیتی، این روش خطرناک است؛ زیرا Client بخشی از محیط غیرقابل اعتماد یا Untrusted محسوب می‌شود.

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

به همین دلیل کنترل امنیتی نباید به این موضوع وابسته باشد که چه Inputهایی در HTML یا برنامه موبایل نمایش داده شده‌اند. Auto Binding چگونه به Mass Assignment منجر می‌شود؟

Auto Binding چگونه به Mass Assignment منجر می‌شود؟

فرض کنید مدل داخلی یک حساب کاربری دارای Propertyهای زیر باشد:

id
name
email
avatar
role
status
emailVerified
organizationId
createdAt
updatedAt

از طرف دیگر صفحه ویرایش حساب فقط امکان تغییر این سه مورد را ارائه می‌دهد:

name
avatar
email

اگر Controller کل Request Body را به User Model منتقل کند، برنامه عملاً فرض کرده است که هر Property موجود در Model برای Client قابل اعتماد است.

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

const user = await User.findById(userId);

Object.assign(user, req.body);

await user.save();

مشکل این کد در خود Object.assign نیست؛ مشکل این است که req.body بدون تعریف Boundary مشخص به Domain Object منتقل شده است.

راه امن‌تر این است که برنامه دقیقاً مشخص کند چه اطلاعاتی اجازه عبور دارند:

const input = {
  displayName: req.body.displayName,
  avatar: req.body.avatar,
  timezone: req.body.timezone
};

user.displayName = input.displayName;
user.avatar = input.avatar;
user.timezone = input.timezone;

await user.save();

در پروژه‌های بزرگ معمولاً این مسئولیت به DTO، Input Model، Serializer یا Mapper اختصاص داده می‌شود.

چرا Mass Assignment در APIها اهمیت بیشتری دارد؟

APIها معمولاً ساختار داده را به شکل واضح‌تری نسبت به صفحات سنتی وب نمایش می‌دهند.

در یک API ممکن است Client داده را به شکل JSON دریافت کند و همان Object را با ساختاری مشابه برای Update دوباره ارسال کند.

برای مثال پاسخ یک Endpoint ممکن است اطلاعات متعددی درباره یک حساب داشته باشد:

{
  "id": 1205,
  "displayName": "User",
  "timezone": "Asia/Tehran",
  "accountStatus": "active",
  "emailVerified": true
}

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

بنابراین توسعه‌دهنده نباید بر مخفی‌بودن نام Propertyها به‌عنوان یک کنترل امنیتی حساب کند.

اصل مهم این است:

Property حساس باید حتی در صورت شناخته‌شدن نام آن نیز غیرقابل تغییر باشد.

امنیت API نباید بر پایه ناشناخته‌بودن نام فیلدهایی مانند role، verified یا status طراحی شود.

چه Propertyهایی در Mass Assignment حساس هستند؟

حساس‌بودن یک Property کاملاً به منطق برنامه بستگی دارد، اما چند گروه معمولاً باید با دقت بیشتری کنترل شوند.

Propertyهای مربوط به نقش و سطح دسترسی

برای مثال:

role
isAdmin
permissionLevel
accessLevel
isModerator

تغییر این نوع Propertyها ممکن است مستقیماً روی Authorization تأثیر بگذارد.

در طراحی امن، تعیین Role باید از مسیر مدیریتی مشخص و پس از بررسی Permission انجام شود، نه از Endpoint عمومی ویرایش پروفایل.

Propertyهای مالی

در سیستم‌های فروشگاهی، کیف پول، اشتراک و سرویس‌های مالی Propertyهایی مانند موارد زیر حساس هستند:

balance
credit
price
discount
paid
paymentStatus
subscriptionLevel

مقدار این فیلدها معمولاً باید نتیجه یک Business Process معتبر باشد.

برای مثال وضعیت پرداخت نباید صرفاً براساس مقدار ارسال‌شده توسط Client تغییر کند؛ این وضعیت باید توسط سیستم پرداخت یا منطق داخلی سرور تعیین شود.

Propertyهای وضعیت فرایند

برخی فیلدها وضعیت یک Workflow را مشخص می‌کنند:

approved
published
verified
completed
reviewStatus
orderStatus

اگر یک Endpoint عمومی امکان تغییر این Propertyها را فراهم کند، کاربر ممکن است مرحله‌ای از فرایند را دور بزند.

برای نمونه، وضعیت «تأیید شده» باید توسط مرحله تأیید مربوطه ایجاد شود، نه صرفاً از داده‌ای که در Request دریافت می‌شود.

Propertyهای مالکیت

یکی از مهم‌ترین گروه‌ها عبارت‌اند از:

ownerId
userId
accountId
organizationId
tenantId
teamId

این فیلدها تعیین می‌کنند یک Resource متعلق به چه فرد، حساب یا سازمانی است.

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

برای مثال:

tenantId = authenticatedUser.tenantId

به‌مراتب امن‌تر از اعتماد به مقداری است که Client ارسال می‌کند.

Propertyهای سیستمی و Audit

فیلدهایی مانند:

createdAt
updatedAt
createdBy
deletedAt
lastLogin
auditStatus

معمولاً باید توسط خود سیستم مدیریت شوند.

قابل‌ویرایش بودن این اطلاعات می‌تواند Logging، Audit Trail یا تحقیقات امنیتی را دچار مشکل کند.

تفاوت Mass Assignment با Input Validation چیست؟

یکی از اشتباهات رایج این است که توسعه‌دهنده تصور کند داشتن سیستم Validation به‌تنهایی از Mass Assignment جلوگیری می‌کند.

این دو کنترل هدف متفاوتی دارند.

Input Validation بررسی می‌کند که مقدار یک فیلد معتبر است یا خیر.

برای مثال:

email باید قالب معتبر داشته باشد.
سن باید عدد باشد.
نام نباید از طول مشخصی بیشتر باشد.

اما Mass Assignment درباره سؤال دیگری است:

آیا کاربر اصولاً اجازه دارد این Property را تغییر دهد؟

فرض کنید مقدار role فقط می‌تواند یکی از گزینه‌های زیر باشد:

user
editor
admin

حتی اگر Validator به‌درستی بررسی کند که مقدار ارسال‌شده یکی از این سه گزینه است، هنوز مشکل امنیتی حل نشده است.

چرا؟

زیرا کاربر عادی شاید اصلاً نباید اجازه تغییر role را داشته باشد.

بنابراین:

Validation ≠ Authorization

اعتبارسنجی مقدار جایگزین کنترل مجوز تغییر Property نمی‌شود. تفاوت Mass Assignment با BOLA و IDOR چیست؟

تفاوت Mass Assignment با BOLA و IDOR چیست؟

Mass Assignment و Broken Object Level Authorization یا BOLA گاهی با یکدیگر اشتباه گرفته می‌شوند.

در BOLA سؤال اصلی این است:

آیا کاربر اجازه دسترسی یا انجام عملیات روی این Object را دارد؟

برای مثال آیا User A می‌تواند Resource متعلق به User B را مشاهده یا ویرایش کند؟

اما در Mass Assignment سؤال این است:

اگر کاربر اجازه ویرایش این Object را دارد، کدام Propertyهای آن Object را اجازه دارد تغییر دهد؟

فرض کنید کاربر مجاز است پروفایل خودش را ویرایش کند.

پس Object-Level Authorization صحیح است.

اما شاید فقط اجازه تغییر نام و تصویر پروفایل را داشته باشد، نه Role یا وضعیت تأیید.

در این شرایط ممکن است BOLA وجود نداشته باشد ولی Property-Level Authorization ناقص باشد و آسیب‌پذیری Mass Assignment شکل بگیرد.

به همین دلیل کنترل امنیتی باید حداقل دو سطح داشته باشد:

آیا این کاربر به این Object دسترسی دارد؟
                ↓
کدام Propertyهای این Object قابل تغییر هستند؟

هر دو سؤال باید مستقل پاسخ داده شوند.

تفاوت Mass Assignment با Excessive Data Exposure

Excessive Data Exposure بیشتر به اطلاعات خروجی مربوط است.

در این مشکل، API اطلاعات بیشتری از مقدار موردنیاز Client بازمی‌گرداند.

برای مثال صفحه پروفایل فقط به نام و Avatar نیاز دارد، اما API کل Object کاربر را شامل اطلاعات داخلی بازمی‌گرداند.

Mass Assignment در سمت ورودی اتفاق می‌افتد و درباره Propertyهایی است که Client اجازه تغییر آن‌ها را پیدا می‌کند.

بااین‌حال این دو ضعف می‌توانند یکدیگر را تقویت کنند.

اگر Response شامل Propertyهای داخلی باشد، شناخت ساختار Object برای بررسی امنیتی ساده‌تر می‌شود.

بنابراین در طراحی API دو اصل مهم وجود دارد:

ورودی: فقط Propertyهای موردنیاز را قبول کن.
خروجی: فقط Propertyهای موردنیاز را برگردان.

آیا Mass Assignment همان Injection است؟

خیر.

اگرچه در بعضی منابع یا Frameworkها اصطلاحاتی مانند Object Injection دیده می‌شود، Mass Assignment با آسیب‌پذیری‌هایی مانند SQL Injection یا Command Injection تفاوت دارد.

در SQL Injection، ورودی کاربر می‌تواند ساختار Query را تحت تأثیر قرار دهد.

در Command Injection، ورودی ممکن است به اجرای Command ناخواسته منجر شود.

اما در Mass Assignment، مسئله اصلی معمولاً تغییر غیرمجاز Propertyهای یک Object معتبر است.

ممکن است Mass Assignment در شرایط خاص به آسیب‌پذیری‌های دیگری متصل شود، اما ماهیت اصلی آن کنترل ناکافی بر ویژگی‌های قابل تغییر است.

Mass Assignment و Prototype Pollution چه تفاوتی دارند؟

این دو آسیب‌پذیری ممکن است در برخی پیاده‌سازی‌های JavaScript به یکدیگر نزدیک شوند، اما یکسان نیستند.

Mass Assignment معمولاً درباره این است که Client بتواند Propertyهایی از یک Object را تغییر دهد که نباید قابل کنترل باشند.

Prototype Pollution زمانی مطرح می‌شود که برنامه اجازه دهد Propertyهای Prototype در JavaScript به‌صورت ناخواسته تغییر کنند و این تغییر روی Objectهای دیگر اثر بگذارد.

بنابراین هرچند پردازش ناامن Propertyهای پویا می‌تواند زمینه مشترکی میان این مسائل ایجاد کند، دفاع در برابر Mass Assignment نباید صرفاً به مسدودکردن Propertyهای خاص JavaScript محدود شود.

چرا استفاده مستقیم از Database Model خطرناک است؟

یکی از ریشه‌های معماری Mass Assignment استفاده از یک کلاس واحد برای چند وظیفه متفاوت است.

فرض کنید کلاس User هم‌زمان برای موارد زیر استفاده شود:

  • ذخیره اطلاعات در Database
  • دریافت Request
  • ارسال Response
  • اجرای Business Logic
  • کنترل اطلاعات پنل مدیریت

در ابتدا این طراحی ساده به نظر می‌رسد.

اما با رشد برنامه، Model شامل تعداد زیادی Property خواهد شد که برخی فقط برای Backend یا Administrator هستند.

اگر همان Model مستقیماً به Request Binding متصل باشد، هر Property جدید می‌تواند ناخواسته سطح حمله Endpointهای قدیمی را تغییر دهد.

به همین دلیل اضافه‌شدن یک فیلد جدید مانند:

accountLocked

نباید بدون تغییر Controller باعث شود Endpoint قدیمی ویرایش پروفایل قادر به دریافت این Property شود.

این اصل را می‌توان چنین بیان کرد:

تغییر Schema داخلی نباید به‌صورت خودکار قرارداد امنیتی API را گسترش دهد. DTO چگونه از Mass Assignment جلوگیری می‌کند؟

DTO چگونه از Mass Assignment جلوگیری می‌کند؟

DTO یا Data Transfer Object یکی از مؤثرترین روش‌های معماری برای کاهش خطر Mass Assignment است.

به‌جای اینکه Request مستقیماً به Domain Model تبدیل شود، یک Object مخصوص ورودی ساخته می‌شود.

برای مثال Domain Model ممکن است شامل این Propertyها باشد:

User
├── id
├── displayName
├── email
├── role
├── status
├── emailVerified
├── tenantId
├── createdAt
└── updatedAt

اما DTO مخصوص ویرایش پروفایل فقط شامل موارد زیر باشد:

UpdateProfileDTO
├── displayName
├── avatar
└── timezone

در نتیجه حتی اگر Domain Model در آینده دارای Propertyهای بیشتری شود، آن Propertyها به‌صورت خودکار وارد قرارداد Endpoint نمی‌شوند.

این جداسازی چند مزیت مهم دارد:

  1. ورودی قابل قبول Endpoint دقیقاً مشخص می‌شود.
  2. Domain Model مستقیماً در معرض Client قرار نمی‌گیرد.
  3. Validation ساده‌تر می‌شود.
  4. مستندسازی API دقیق‌تر خواهد بود.
  5. تست امنیتی آسان‌تر می‌شود.
  6. تغییر Database Schema الزاماً API Contract را تغییر نمی‌دهد.
Allowlist بهتر است یا Denylist؟

Allowlist بهتر است یا Denylist؟

دو راه کلی برای کنترل Propertyها وجود دارد.

Denylist

در Denylist توسعه‌دهنده مشخص می‌کند کدام فیلدها غیرمجاز هستند.

مثلاً:

role
isAdmin
balance

مسئله این روش آن است که با اضافه‌شدن یک Property حساس جدید ممکن است توسعه‌دهنده فراموش کند آن را به فهرست ممنوع اضافه کند.

مثلاً چند ماه بعد فیلدی با نام زیر اضافه شود:

canApprovePayment

اگر Denylist به‌روزرسانی نشود، Endpoint قدیمی ممکن است ناخواسته آن را قبول کند.

Allowlist

در Allowlist فقط Propertyهایی که واقعاً برای Endpoint لازم هستند مشخص می‌شوند:

displayName
avatar
timezone

هر Property دیگری به‌صورت پیش‌فرض رد یا نادیده گرفته می‌شود.

از دید امنیتی این رویکرد معمولاً مقاوم‌تر است، زیرا اصل Default Deny را دنبال می‌کند.

یعنی:

هیچ Property قابل تغییر نیست،
مگر اینکه صراحتاً اجازه داده شده باشد.

آیا Ignore کردن Propertyهای ناشناخته کافی است؟

بستگی به طراحی API دارد، اما در بسیاری از سیستم‌ها بهتر است Requestهایی که Property غیرمنتظره دارند به‌صورت مشخص مدیریت شوند.

دو رفتار رایج وجود دارد:

Ignore Unknown Fields
Reject Unknown Fields

نادیده‌گرفتن فیلد ناشناخته می‌تواند سازگاری Clientها را ساده‌تر کند، اما تشخیص خطاهای Integration و درخواست‌های غیرعادی را دشوارتر می‌کند.

Reject کردن Property ناشناخته معمولاً Contract سخت‌گیرانه‌تری ایجاد می‌کند.

برای APIهای حساس، استفاده از Schema Validation و تعریف دقیق Propertyهای مجاز می‌تواند لایه دفاعی مفیدی باشد.

برای مثال در قراردادهای مبتنی بر JSON Schema، در صورت سازگاری معماری و Clientها می‌توان مشخص کرد که Propertyهای خارج از Schema پذیرفته نشوند.

بااین‌حال Schema Validation به‌تنهایی Authorization نیست.

حتی اگر Property در Schema تعریف شده باشد، ممکن است همه کاربران اجازه تغییر آن را نداشته باشند.

Property-Level Authorization چیست؟

Authorization معمولاً در سطح Endpoint یا Object تصور می‌شود:

آیا کاربر Login کرده است؟
آیا اجازه ویرایش این Resource را دارد؟

اما APIهای پیچیده ممکن است نیازمند کنترل در سطح Property باشند.

فرض کنید Administrator و User هر دو از Endpoint مشابهی برای Update استفاده می‌کنند.

کاربر عادی اجازه دارد:

displayName
avatar
timezone

را تغییر دهد.

Administrator علاوه بر آن ممکن است اجازه تغییر:

accountStatus
moderationState

را نیز داشته باشد.

پس مجموعه Propertyهای قابل تغییر باید به Context مجوز وابسته باشد.

یک مدل ساده می‌تواند چنین باشد:

User:
  allowedFields = [displayName, avatar, timezone]

Admin:
  allowedFields = [displayName, avatar, timezone, accountStatus]

در پروژه‌های حساس‌تر بهتر است Use Caseهای مدیریتی و عمومی حتی Endpointهای جداگانه داشته باشند تا مرزهای امنیتی واضح‌تر شوند.

چرا جداسازی Endpointهای مدیریتی اهمیت دارد؟

استفاده از یک Endpoint همه‌کاره مانند:

PATCH /users/{id}

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

این Endpoint ممکن است نیاز داشته باشد:

  • کاربر نام خودش را تغییر دهد.
  • مدیر وضعیت حساب را تغییر دهد.
  • سیستم وضعیت تأیید را تغییر دهد.
  • سرویس پرداخت سطح اشتراک را به‌روزرسانی کند.

قرار دادن تمام این مسئولیت‌ها در یک مسیر، احتمال خطای Authorization و Mass Assignment را افزایش می‌دهد.

طراحی واضح‌تر می‌تواند Use Caseها را جدا کند:

Update Profile
Change Account Status
Verify Email
Change Subscription
Assign Role

در چنین معماری‌ای هر Command ورودی مخصوص، Authorization مخصوص و Business Rule مشخصی دارد.

Mass Assignment در Laravel

Laravel یکی از Frameworkهایی است که مفهوم Mass Assignment را به‌صورت مشخص در Eloquent Model مدیریت می‌کند.

در این نوع معماری، عملیات‌هایی که مجموعه‌ای از Attributeها را روی Model اعمال می‌کنند باید با دقت کنترل شوند.

یک روش رایج تعریف Propertyهای قابل تخصیص است.

از دید طراحی امنیتی، نکته مهم این نیست که صرفاً تنظیمات Framework فعال باشد؛ بلکه باید مشخص شود هر مسیر ورودی چه فیلدهایی را مجاز می‌داند.

همچنین نباید تصور کرد استفاده از قابلیت‌های محافظتی ORM جایگزین Authorization می‌شود.

حتی یک Property قابل‌تخصیص ممکن است فقط برای نقش خاصی مجاز باشد.

بنابراین ترکیب مناسب شامل موارد زیر است:

Request Validation
+
Authorization
+
Allowlisted Fields
+
Business Logic

Mass Assignment در Ruby on Rails

در Rails مفهوم Mass Assignment سابقه شناخته‌شده‌ای دارد.

الگوی Strong Parameters برای کنترل فیلدهایی طراحی شده است که Controller اجازه عبور آن‌ها را می‌دهد.

از نظر معماری امن، Controller نباید کل Parameters دریافتی را بدون محدودسازی به Model انتقال دهد.

هر Action باید مجموعه مشخصی از پارامترهای مجاز داشته باشد.

بااین‌حال همان اصل عمومی همچنان برقرار است:

مجازبودن یک Parameter در Controller به این معنا نیست که تمام کاربران مجاز به تغییر آن هستند.

Role و Context کاربر همچنان باید در Authorization بررسی شود.

Mass Assignment در ASP.NET Core

ASP.NET Core دارای Model Binding قدرتمندی است که می‌تواند داده‌های HTTP را به Objectهای برنامه تبدیل کند.

این قابلیت توسعه API را بسیار ساده می‌کند، اما Bind کردن مستقیم Entityهای پایگاه داده به ورودی API می‌تواند Overposting Risk ایجاد کند.

استفاده از Input Model یا ViewModel اختصاصی کمک می‌کند Propertyهای قابل دریافت محدود شوند.

برای مثال به‌جای:

UserEntity

می‌توان ورودی مستقلی مانند:

UpdateProfileRequest

داشت که فقط اطلاعات موردنیاز عملیات در آن تعریف شده باشد.

این روش علاوه بر امنیت، نگهداری پروژه را نیز ساده‌تر می‌کند.

Mass Assignment در Spring

Spring MVC نیز قابلیت Data Binding دارد و می‌تواند پارامترهای Request را به Objectهای Java متصل کند.

در پروژه‌های Spring بهتر است Object دریافت‌کننده Request صرفاً شامل داده‌هایی باشد که برای همان عملیات لازم هستند.

استفاده مستقیم از Entityهای JPA به‌عنوان Request Model معمولاً باعث افزایش Coupling میان HTTP Layer و Persistence Layer می‌شود.

DTO اختصاصی، Validation و Mapping صریح می‌توانند این مرز را واضح‌تر کنند.

در مواردی که Data Binding پیشرفته استفاده می‌شود، قابلیت‌های محدودسازی فیلدهای قابل Binding نیز باید براساس نیاز پروژه بررسی شوند.

Mass Assignment در Django و Django REST Framework

در Django و DRF نیز طراحی Serializer یا Form اهمیت زیادی دارد.

اگر Model دارای Propertyهای حساس باشد، Serializer نباید بدون بررسی تمام فیلدها را قابل نوشتن کند.

بهتر است Fieldهای ورودی صراحتاً مشخص شوند و Propertyهایی که فقط باید توسط Server تعیین شوند به‌عنوان Read Only یا خارج از Input تعریف شوند.

برای مثال:

created_by
owner
verification_status
role

معمولاً بهتر است از Context احراز هویت یا منطق داخلی Server تعیین شوند.

Mass Assignment در Node.js و JavaScript

در پروژه‌های Node.js مشکل اغلب زمانی دیده می‌شود که Developer از Spread Operator، Object Assignment یا Updateهای عمومی ORM/ODM روی کل Request Body استفاده کند.

الگوهایی مانند مفهوم زیر باید با احتیاط بررسی شوند:

update(req.body)

یا:

{ ...req.body }

اگر مقصد یک Object حساس باشد، تمام Propertyهای ورودی ممکن است وارد مرحله بعد شوند.

روش امن‌تر این است که Object جدیدی فقط از Propertyهای مجاز ساخته شود یا یک Schema سخت‌گیرانه برای ورودی تعریف شود.

کتابخانه Validation به‌تنهایی کافی نیست مگر آنکه واقعاً فیلدهای غیرمجاز را کنترل کند و Authorization جداگانه نیز اجرا شود.

Mass Assignment در GraphQL

GraphQL به دلیل داشتن Schema مشخص می‌تواند در بعضی معماری‌ها کنترل دقیق‌تری روی Inputها ایجاد کند، اما استفاده از GraphQL به‌خودی‌خود Mass Assignment را حذف نمی‌کند.

ریسک معمولاً در Resolver ایجاد می‌شود.

برای مثال اگر یک Mutation دارای Input عمومی باشد و Resolver کل Input را مستقیماً به ORM منتقل کند، همچنان امکان ایجاد مرز امنیتی ضعیف وجود دارد.

بهتر است هر Mutation دارای Input Type متناسب با همان عملیات باشد.

برای نمونه:

UpdateProfileInput
ChangePasswordInput
ChangeAccountStatusInput

بهتر از یک Input عمومی مانند:

UpdateUserInput

با ده‌ها Property متفاوت است.

همچنین Resolver باید Authorization مربوط به عملیات و Propertyهای حساس را جداگانه بررسی کند.

PATCH و خطر Mass Assignment

Endpointهای PATCH به دلیل ماهیت Partial Update باید با دقت ویژه طراحی شوند.

هدف PATCH این است که Client بتواند بخشی از Resource را تغییر دهد.

اما «بخشی از Resource» نباید به معنای «هر Property موجود در Resource» تفسیر شود.

برای هر Endpoint باید محدوده Patchable Fields مشخص باشد.

این موضوع در ساختارهایی مانند JSON Merge Patch یا JSON Patch نیز اهمیت دارد.

پیاده‌سازی نباید صرفاً مسیر Property دریافت‌شده از Client را روی Object داخلی اعمال کند.

قبل از اعمال هر تغییر باید بررسی شود:

آیا Property مجاز است؟
آیا کاربر اجازه تغییر آن را دارد؟
آیا مقدار معتبر است؟
آیا تغییر با Business Rule سازگار است؟

Mass Assignment در سیستم‌های Multi-Tenant

در برنامه‌های Multi-Tenant، Propertyهایی مانند tenantId، organizationId یا workspaceId بسیار حساس هستند.

یکی از اصول مهم این است که Tenant Context تا حد امکان از Session، Token یا Context سمت Server استخراج شود.

برای مثال ایجاد یک Document می‌تواند به این شکل طراحی شود:

document.tenantId = authenticatedContext.tenantId

نه اینکه مقدار Tenant مستقیماً از Request کاربر پذیرفته شود.

همین مسئله درباره Owner نیز صدق می‌کند.

اگر Resource متعلق به User جاری است، معمولاً ownerId باید توسط Server تعیین شود.

این طراحی احتمال تغییر غیرمجاز مالکیت یا عبور داده میان Tenantها را کاهش می‌دهد.

Mass Assignment در Microserviceها

در معماری Microservice ممکن است تصور شود درخواست‌های داخلی قابل اعتماد هستند.

این فرض می‌تواند خطرناک باشد.

یک Service ممکن است Object بزرگی را از Gateway یا Service دیگر دریافت کرده و آن را مستقیماً به Entity داخلی تبدیل کند.

در این حالت Mass Assignment می‌تواند از مرز یک سرویس به سرویس دیگر منتقل شود.

هر Service باید Contract ورودی خودش را داشته باشد.

اصل Zero Trust در ارتباطات داخلی نیز کاربرد دارد:

Service A داده‌ای ارسال می‌کند
        ↓
Service B آن را براساس Contract خودش اعتبارسنجی می‌کند

نه اینکه Service B تمام داده را صرفاً به دلیل داخلی‌بودن منبع قابل اعتماد فرض کند.

Event-Driven Architecture و Mass Assignment

مشکل مشابهی در Message Queue و Event Bus نیز دیده می‌شود.

فرض کنید یک Consumer پیام دریافتی را مستقیماً روی Entity پایگاه داده Merge کند.

با تغییر Schema پیام یا اضافه‌شدن Property جدید ممکن است Consumer بدون قصد قبلی اطلاعات بیشتری را ذخیره کند.

برای Eventها نیز بهتر است:

  • Schema مشخص وجود داشته باشد.
  • Event Versioning مدیریت شود.
  • Mapping صریح انجام شود.
  • Propertyهای داخلی از داده Event جدا باشند.
  • Consumer فقط داده موردنیاز را دریافت کند.

مرز اعتماد فقط HTTP نیست.

هر ورودی خارج از Component باید به‌عنوان یک Trust Boundary در نظر گرفته شود. چگونه از آسیب‌پذیری Mass Assignment جلوگیری کنیم؟

چگونه از آسیب‌پذیری Mass Assignment جلوگیری کنیم؟

پیشگیری مؤثر معمولاً ترکیبی از چند کنترل است.

هیچ کنترل واحدی نباید به‌تنهایی مسئول امنیت باشد.

۱. استفاده از DTO یا Input Model اختصاصی

برای هر عملیات Object ورودی مشخص بسازید.

برای مثال:

RegisterUserDTO
UpdateProfileDTO
ChangePasswordDTO
CreateOrderDTO

هرکدام فقط Propertyهای موردنیاز همان عملیات را داشته باشند.

۲. استفاده از Allowlist

Propertyهای قابل تغییر را صراحتاً مشخص کنید.

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

۳. Mapping صریح

در عملیات حساس، داده DTO را به‌صورت صریح به Domain Model منتقل کنید.

این کار ممکن است کمی کد بیشتری ایجاد کند، اما Boundary امنیتی را روشن می‌کند.

۴. Propertyهای داخلی را Server-Side تعیین کنید

مواردی مانند:

ownerId
tenantId
createdBy
paymentStatus
emailVerified
role

نباید در Endpoint عمومی از Client گرفته شوند مگر اینکه Business Requirement مشخصی وجود داشته باشد.

۵. Authorization را جدا از Validation اجرا کنید

بررسی کنید:

کاربر چه عملیاتی می‌تواند انجام دهد؟
روی کدام Object؟
روی کدام Property؟

۶. Schema ورودی را محدود کنید

OpenAPI، JSON Schema، GraphQL Input Type و ابزارهای مشابه می‌توانند Contract ورودی را دقیق‌تر کنند.

در صورت سازگاری سیستم، Propertyهای ناشناخته نیز می‌توانند Reject شوند.

۷. Entity را مستقیماً به API متصل نکنید

Persistence Model نباید لزوماً همان Public API Model باشد.

جداسازی این دو لایه باعث کاهش Coupling و سطح حمله می‌شود.

۸. Propertyهای خروجی را نیز محدود کنید

Response DTO یا Serializer اختصاصی داشته باشید.

این کار اطلاعات داخلی غیرضروری را از Client دور نگه می‌دارد.

۹. Business Logic را در Service Layer نگه دارید

Controller نباید فقط Request را بگیرد و مستقیماً Database را Update کند.

مسیر بهتر:

Controller
   ↓
Validation
   ↓
Authorization
   ↓
Application Service
   ↓
Domain Rules
   ↓
Repository

است.

۱۰. برای عملیات حساس Endpoint جداگانه بسازید

تغییر Role، تأیید حساب یا تغییر وضعیت مالی بهتر است عملیات مشخص خود را داشته باشد. معماری امن در برابر Mass Assignment

معماری امن در برابر Mass Assignment

یک معماری مقاوم می‌تواند جریان زیر را دنبال کند:

Untrusted Client
       ↓
Authentication
       ↓
Authorization
       ↓
Request Schema
       ↓
DTO / Input Model
       ↓
Validation
       ↓
Explicit Mapping
       ↓
Business Logic
       ↓
Domain Model
       ↓
Repository
       ↓
Database

در مسیر Response نیز بهتر است جریان معکوس مستقیماً Entity را به Client برنگرداند:

Database Entity
      ↓
Response Mapper
      ↓
Response DTO
      ↓
Client

این جداسازی باعث می‌شود مدل داخلی برنامه با قرارداد عمومی API یکی نباشد.

آیا WAF می‌تواند جلوی Mass Assignment را بگیرد؟

WAF یا Web Application Firewall می‌تواند برخی الگوهای غیرعادی Request را شناسایی کند، اما راهکار اصلی Mass Assignment نیست.

دلیل ساده است.

یک Request مربوط به Mass Assignment ممکن است از نظر Syntax کاملاً معتبر باشد.

JSON معتبر است.

HTTP Method معتبر است.

Authentication نیز معتبر است.

حتی مقدار Property نیز می‌تواند از نظر نوع داده صحیح باشد.

مشکل در Business Authorization است.

WAF معمولاً نمی‌داند یک User معمولی اجازه تغییر کدام Property داخلی Domain Model را دارد.

بنابراین دفاع اصلی باید در خود Application انجام شود.

WAF می‌تواند لایه مکمل باشد، نه جایگزین طراحی امن.

آیا Frontend Validation از Mass Assignment جلوگیری می‌کند؟

خیر.

مخفی‌کردن یک Input در رابط کاربری کنترل امنیتی محسوب نمی‌شود.

برای مثال اگر پنل کاربر Input مربوط به role را نمایش ندهد، این موضوع به‌تنهایی تضمین نمی‌کند Backend آن Property را قبول نمی‌کند.

Backend باید فرض کند Client قابل تغییر است.

این اصل درباره موارد زیر صدق می‌کند:

  • وب‌سایت
  • اپلیکیشن موبایل
  • SPA
  • Desktop Client
  • Browser Extension
  • API Consumer

Authorization همیشه باید Server-Side اجرا شود.

چگونه Mass Assignment را در تست امنیتی بررسی کنیم؟

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

هدف تست دفاعی این است که مشخص شود آیا Endpoint فقط Propertyهای موردانتظار را می‌پذیرد یا خیر.

مرحله اول: بررسی قرارداد Endpoint

برای هر Endpoint مشخص کنید:

چه Objectی ایجاد یا ویرایش می‌شود؟
چه Propertyهایی در Request تعریف شده‌اند؟
کدام Propertyها Server-Controlled هستند؟

مستندات OpenAPI یا Schema می‌توانند در این مرحله مفید باشند.

مرحله دوم: مقایسه Input Model و Domain Model

اگر Domain Model شامل ۲۰ Property است ولی Endpoint باید فقط ۳ Property را تغییر دهد، بررسی کنید آیا این جداسازی واقعاً در کد اعمال شده است.

وجود DTO نشانه خوبی است، اما باید مسیر Mapping نیز بررسی شود.

مرحله سوم: Negative Testing

برای فیلدهایی که Client نباید کنترل کند تست منفی بنویسید.

انتظار می‌رود برنامه آن‌ها را:

Reject

کند یا براساس قرارداد مشخص نادیده بگیرد.

مهم‌تر از همه این است که مقدار نهایی Property حساس تغییر نکند.

مرحله چهارم: بررسی Roleهای مختلف

یک Endpoint ممکن است برای Administrator امن باشد ولی برای User عادی بیش از حد اجازه تغییر داشته باشد.

بنابراین تست‌ها باید براساس Role Matrix طراحی شوند.

برای مثال:

PropertyUserModeratorAdminSystem
displayNameمجازمجازمجازمجاز
avatarمجازمجازمجازمجاز
moderationStatusغیرمجازمحدودمجازمجاز
roleغیرمجازغیرمجازمجازمجاز
paymentStatusغیرمجازغیرمجازغیرمجازمجاز
createdAtغیرمجازغیرمجازغیرمجازمجاز

چنین ماتریسی هم برای توسعه و هم برای تست امنیتی بسیار ارزشمند است.

تست خودکار برای جلوگیری از بازگشت آسیب‌پذیری

مشکلات Mass Assignment ممکن است پس از Refactor یا اضافه‌شدن Property جدید دوباره ایجاد شوند.

بنابراین تست امنیتی باید بخشی از Test Suite باشد.

برای هر Endpoint حساس می‌توان بررسی کرد:

  • Propertyهای مجاز تغییر می‌کنند.
  • Propertyهای غیرمجاز تغییر نمی‌کنند.
  • Property ناشناخته مطابق Contract مدیریت می‌شود.
  • User عادی قادر به تغییر Field مدیریتی نیست.
  • Tenant Context از Request عمومی گرفته نمی‌شود.
  • Owner از Authentication Context استخراج می‌شود.
  • تغییر Model داخلی API Contract را ناخواسته تغییر نمی‌دهد.

این تست‌ها به‌خصوص هنگام اضافه‌شدن Migrationهای جدید اهمیت دارند.

نقش OpenAPI در کاهش خطر Mass Assignment

OpenAPI می‌تواند Contract هر Endpoint را مستند کند.

اشتباه رایج این است که یک Schema بزرگ مانند User برای همه عملیات استفاده شود:

GET User
POST User
PATCH User
Admin Update User

این طراحی باعث می‌شود مرز خواندن و نوشتن مبهم شود.

بهتر است Schemaهای جداگانه‌ای تعریف شوند:

UserResponse
CreateUserRequest
UpdateProfileRequest
AdminUpdateUserRequest

با این روش مشخص می‌شود چه Propertyهایی متعلق به Response و چه Propertyهایی متعلق به Input هستند.

البته مستندات به‌تنهایی کنترل امنیتی محسوب نمی‌شوند.

Backend باید همان Contract را واقعاً Enforcement کند.

نقش پایگاه داده در دفاع

Database Constraintها لایه امنیتی مفیدی هستند، اما Mass Assignment را به‌تنهایی حل نمی‌کنند.

برای مثال Foreign Key می‌تواند مانع برخی داده‌های نامعتبر شود، اما مشخص نمی‌کند کدام User اجازه تغییر آن Foreign Key را دارد.

همچنین NOT NULL، CHECK Constraint و Type Constraint درباره Authorization تصمیم نمی‌گیرند.

بااین‌حال پایگاه داده می‌تواند به‌عنوان Defense in Depth استفاده شود.

برای مثال:

  • Integrity Constraint
  • Unique Constraint
  • محدودیت Relationship
  • Transaction
  • Immutable Audit Records

می‌توانند تأثیر برخی خطاهای Application Layer را کاهش دهند.

Logging مناسب برای شناسایی Mass Assignment

ثبت Log امنیتی باید بر رویدادهای مهم تمرکز کند.

برای مثال تغییر Propertyهایی مانند:

role
accountStatus
ownerId
tenantId
paymentStatus
verificationStatus

می‌تواند به‌عنوان Security Event ثبت شود.

Log مناسب بهتر است شامل Context لازم مانند Actor، Resource، زمان و نوع عملیات باشد.

البته اطلاعات حساس مانند Password، Token، Session ID یا Secret نباید بدون ملاحظات لازم در Log ذخیره شوند.

Monitoring می‌تواند تغییرات غیرمنتظره Propertyهای حساس را شناسایی کند.

در صورت کشف Mass Assignment چه اقداماتی لازم است؟

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

ابتدا باید مشخص شود چه Endpointهایی از همان الگوی Binding استفاده می‌کنند.

سپس Scope بررسی شود:

کدام Modelها در معرض Binding بوده‌اند؟
کدام Propertyها حساس هستند؟
چه Roleهایی به Endpoint دسترسی داشته‌اند؟
آیا Log تغییرات وجود دارد؟
آیا تغییر غیرمجاز قبلاً اتفاق افتاده است؟

پس از آن باید Root Cause اصلاح شود.

اگر علت اصلی استفاده مستقیم از Entity به‌عنوان Request Model بوده، بهتر است معماری اصلاح شود نه اینکه فقط چند Property به Denylist اضافه شوند.

همچنین Audit داده‌های حساس اهمیت دارد.

برای نمونه ممکن است لازم باشد تغییرات تاریخی Role، Ownership یا وضعیت مالی بررسی شوند.

اشتباهات رایج در جلوگیری از Mass Assignment

تکیه کامل بر Denylist

فهرست‌کردن Propertyهای خطرناک می‌تواند در کوتاه‌مدت مفید باشد، اما احتمال فراموش‌شدن Propertyهای جدید وجود دارد.

Allowlist معمولاً انتخاب مطمئن‌تری است.

اعتماد به Frontend

نبودن یک Field در Form تضمین نمی‌کند Backend قادر به دریافت آن نیست.

امنیت باید روی Server اعمال شود.

استفاده از Validation به‌جای Authorization

صحیح‌بودن مقدار با مجازبودن تغییر آن متفاوت است.

هر دو کنترل لازم هستند.

استفاده مستقیم از ORM Entity

این کار Boundary میان API و Persistence را تضعیف می‌کند.

DTO یا Input Model اختصاصی معمولاً معماری واضح‌تری ایجاد می‌کند.

استفاده از یک DTO برای همه عملیات

داشتن DTO کافی نیست اگر همان DTO برای Registration، Profile Update و Admin Update استفاده شود.

هر Use Case باید Contract متناسب خودش را داشته باشد.

پذیرش تمام فیلدها و حذف چند مورد حساس

این الگو همچنان Default Allow است.

بهتر است منطق برعکس باشد: فقط Propertyهای موردنیاز انتخاب شوند.

اعتماد کامل به API Gateway یا WAF

Gateway نمی‌تواند تمام Business Ruleهای Domain را درک کند.

کنترل Property باید در Application نیز اجرا شود.

فرض امن‌بودن سرویس‌های داخلی

Internal API، Queue و Microservice نیز Trust Boundary دارند.

Schema و Authorization نباید حذف شوند.

اتکا به نام‌های غیرقابل حدس

تغییر نام isAdmin به نامی پیچیده امنیت ایجاد نمی‌کند.

Security by Obscurity جایگزین Access Control نیست.

چک‌لیست پیشگیری از آسیب‌پذیری Mass Assignment

برای بررسی یک پروژه می‌توان از چک‌لیست زیر استفاده کرد:

  • آیا Request مستقیماً به Database Entity متصل می‌شود؟
  • آیا هر Endpoint Input Model مخصوص خود را دارد؟
  • آیا Propertyهای قابل نوشتن به‌صورت Allowlist تعریف شده‌اند؟
  • آیا Propertyهای حساس فقط Server-Side تعیین می‌شوند؟
  • آیا ownerId و tenantId از Authentication Context استخراج می‌شوند؟
  • آیا Authorization جدا از Validation اجرا می‌شود؟
  • آیا Roleهای مختلف Property-Level Permission متفاوت دارند؟
  • آیا Schema ورودی دقیق است؟
  • آیا Propertyهای ناشناخته مدیریت می‌شوند؟
  • آیا Response فقط اطلاعات موردنیاز را برمی‌گرداند؟
  • آیا عملیات مدیریتی از عملیات عمومی جدا شده‌اند؟
  • آیا تغییرات Propertyهای حساس Audit می‌شوند؟
  • آیا Negative Test برای فیلدهای غیرمجاز وجود دارد؟
  • آیا اضافه‌شدن Property جدید به Entity روی APIهای قدیمی اثر ناخواسته ندارد؟
  • آیا PATCH Endpointها Fieldهای قابل تغییر را محدود می‌کنند؟
  • آیا Resolverهای GraphQL Input را مستقیماً به ORM منتقل نمی‌کنند؟
  • آیا Microserviceها پیام ورودی را براساس Contract خودشان پردازش می‌کنند؟
  • آیا Event Consumerها Mapping صریح دارند؟
  • آیا WAF فقط به‌عنوان لایه مکمل در نظر گرفته شده است؟
  • آیا امنیت سیستم به مخفی‌بودن نام Propertyها وابسته نیست؟

طراحی یک الگوی امن برای Update Profile

برای درک بهتر موضوع، یک Use Case معمولی را در نظر بگیریم.

کاربر می‌خواهد پروفایل خود را ویرایش کند.

در طراحی ضعیف جریان ممکن است چنین باشد:

Request Body
   ↓
User Entity
   ↓
Save

اما طراحی مناسب‌تر چنین ساختاری دارد:

Request
   ↓
UpdateProfileRequest
   ↓
Schema Validation
   ↓
Authenticated User
   ↓
Authorization
   ↓
Profile Service
   ↓
Explicit Mapping
   ↓
User Entity
   ↓
Database

در این مدل، UpdateProfileRequest اصلاً Propertyهایی مانند Role یا Account Status ندارد.

شناسه User نیز از Authentication Context گرفته می‌شود.

در نتیجه Contract عملیات بسیار محدودتر است.

Secure by Design و Mass Assignment

رفع Mass Assignment نباید فقط یک Patch امنیتی در انتهای توسعه باشد.

بهتر است اصل Secure by Design از زمان طراحی API اعمال شود.

هنگام تعریف هر Endpoint سه سؤال باید پاسخ داده شود:

Client چه اطلاعاتی لازم دارد؟

Response باید براساس نیاز واقعی Interface طراحی شود.

Client چه اطلاعاتی اجازه ارسال دارد؟

Input Schema باید مستقل از Database Entity باشد.

Client اجازه تغییر چه چیزی را دارد؟

Authorization باید براساس User، Object، Property و Business State تصمیم بگیرد.

اگر این سه پرسش از ابتدا پاسخ داده شوند، احتمال ایجاد Mass Assignment بسیار کمتر خواهد شد.

اصل Least Privilege در Propertyها

اصل Least Privilege معمولاً درباره دسترسی User یا Process مطرح می‌شود، اما می‌توان آن را به Propertyها نیز تعمیم داد.

هر Endpoint فقط باید حداقل قابلیت نوشتن لازم را داشته باشد.

اگر Update Profile فقط سه Property نیاز دارد، دلیلی ندارد Controller به ده‌ها Property User Entity دسترسی نوشتن بدهد.

این رویکرد Blast Radius یک خطای احتمالی را نیز کاهش می‌دهد.

Mass Assignment و منطق کسب‌وکار

یکی از دلایلی که این آسیب‌پذیری می‌تواند خطرناک باشد این است که Propertyهای حساس اغلب نماینده Business Rule هستند.

برای مثال:

paid = true

فقط یک Boolean نیست.

معنای واقعی آن می‌تواند این باشد:

پرداخت تأیید شده است.

همین مسئله برای:

verified
approved
premium
completed
refunded

نیز وجود دارد.

این مقادیر معمولاً باید نتیجه یک Transition معتبر در State Machine یا Business Process باشند.

بنابراین به‌جای اینکه Client State نهایی را تعیین کند، بهتر است Client درخواست انجام عملیات را ارسال کند.

برای مثال از نظر طراحی:

Request Refund

معنادارتر از این است که Client مستقیماً مقدار:

refunded = true

را تعیین کند.

این تفاوت یکی از اصول مهم طراحی امن API است.

Command-Based API چگونه کمک می‌کند؟

در سیستم‌های حساس می‌توان به‌جای Update عمومی Object، عملیات Domain را به‌صورت Command طراحی کرد.

به‌جای Endpoint بسیار عمومی:

Update Order

Use Caseهای مشخص‌تری ایجاد می‌شوند:

Change Shipping Address
Cancel Order
Approve Order
Confirm Payment

هر Command:

  • ورودی محدود دارد.
  • Authorization مشخص دارد.
  • Business Rule مستقل دارد.
  • Logging مشخص دارد.
  • تست امنیتی ساده‌تری دارد.

این رویکرد به‌خصوص برای سیستم‌های مالی، سازمانی و Multi-Tenant مفید است.

نقش Code Review در شناسایی آسیب‌پذیری Mass Assignment

در Code Review باید الگوهایی که داده ورودی را مستقیماً به Object داخلی منتقل می‌کنند با دقت بررسی شوند.

برای مثال Reviewer باید به عملیات مفهومی شبیه موارد زیر حساس باشد:

Model.update(requestBody)
Object.assign(entity, input)
repository.save(input)
spread entire request into entity
generic mapper from request to database object

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

اما باید بررسی شود:

  • منبع Input چیست؟
  • Schema کجا محدود شده است؟
  • Propertyهای مجاز کجا تعریف شده‌اند؟
  • Authorization در چه مرحله‌ای انجام می‌شود؟
  • Object مقصد چه Propertyهایی دارد؟

Mass Assignment در CI/CD

کنترل این ضعف می‌تواند بخشی از Secure SDLC باشد.

در Pipeline می‌توان از چند روش کمک گرفت:

Static Analysis
Integration Tests
Contract Tests
Security Unit Tests
API Schema Validation
Code Review Rules

برای مثال تیم می‌تواند قانونی داشته باشد که Persistence Entityها مستقیماً در Controllerهای عمومی به‌عنوان Input Type استفاده نشوند.

همچنین Contract Test می‌تواند بررسی کند که Endpoint Propertyهای خارج از Schema را قبول نکند.

نقش Threat Modeling

Mass Assignment موضوع مناسبی برای Threat Modeling است.

هنگام بررسی یک Data Flow مشخص کنید:

داده از کدام Trust Boundary عبور می‌کند؟
به کدام Object تبدیل می‌شود؟
چه Propertyهایی در مقصد وجود دارند؟
کدام Propertyها Security-Sensitive هستند؟

Propertyهایی مانند Role، Ownership، Approval، Price و Status باید به‌عنوان نقاط حساس علامت‌گذاری شوند.

سپس مشخص شود کدام Component مسئول کنترل آن‌هاست.

چه سیستم‌هایی بیشتر در معرض Mass Assignment هستند؟

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

از جمله:

  • REST APIهای بزرگ
  • سیستم‌های دارای ORM
  • پنل‌های مدیریتی
  • SaaSهای Multi-Tenant
  • فروشگاه‌های اینترنتی
  • سیستم‌های مالی
  • پلتفرم‌های اشتراکی
  • شبکه‌های اجتماعی
  • CMSها
  • Microservice Architecture
  • Backendهای موبایل
  • GraphQL APIها
  • سیستم‌های دارای Generic CRUD Endpoint

به‌خصوص Generic CRUD APIها باید با دقت بررسی شوند، زیرا طراحی آن‌ها معمولاً اجازه Update عمومی روی Objectها را ساده می‌کند.

آیا Mass Assignment همیشه باعث Privilege Escalation می‌شود؟

خیر.

شدت آسیب‌پذیری به Property قابل تغییر بستگی دارد.

اگر تنها Property اضافی قابل تغییر یک مقدار کم‌اهمیت باشد، Impact محدود خواهد بود.

اما اگر Property مرتبط با:

Authorization
Ownership
Payment
Verification
Moderation
Tenant Isolation

باشد، پیامد می‌تواند بسیار جدی‌تر شود.

بنابراین Severity باید براساس Business Impact واقعی ارزیابی شود، نه فقط نام آسیب‌پذیری.

سؤالات متداول درباره Mass Assignment

Mass Assignment چیست؟

Mass Assignment زمانی رخ می‌دهد که برنامه داده‌های ورودی Client را بدون محدودسازی کافی به Propertyهای Object داخلی متصل کند و در نتیجه امکان تغییر Propertyهایی فراهم شود که نباید تحت کنترل کاربر باشند.

نام دیگر Mass Assignment چیست؟

بسته به Framework و Context ممکن است اصطلاحاتی مانند Auto Binding، Autobinding یا Overposting نیز در ارتباط با این ضعف استفاده شوند.

آیا Mass Assignment فقط در APIها وجود دارد؟

خیر. هر وب‌اپلیکیشنی که داده Request را به Object داخلی Bind کند می‌تواند در معرض این مشکل باشد. بااین‌حال APIها به دلیل استفاده گسترده از JSON، ORM و Object Mapping یکی از محیط‌های مهم برای این آسیب‌پذیری هستند.

آیا Validation از Mass Assignment جلوگیری می‌کند؟

نه به‌تنهایی. Validation بررسی می‌کند مقدار یک Field معتبر است یا خیر، اما Authorization مشخص می‌کند کاربر اجازه تغییر آن Field را دارد یا نه.

بهترین روش مقابله با Mass Assignment چیست؟

یکی از بهترین الگوها استفاده از DTO یا Input Model اختصاصی، Allowlist فیلدهای قابل تغییر، Mapping صریح و اجرای Authorization در سمت Server است.

آیا Denylist کافی است؟

در بسیاری از پروژه‌ها بهتر است Allowlist استفاده شود، زیرا با Denylist اضافه‌شدن Property حساس جدید می‌تواند باعث ایجاد شکاف امنیتی شود.

آیا WAF می‌تواند Mass Assignment را مسدود کند؟

WAF می‌تواند لایه مکمل باشد، اما معمولاً از Business Ruleهای برنامه اطلاع کافی ندارد. کنترل اصلی باید در Application و Authorization Layer انجام شود.

آیا استفاده از UUID جلوی Mass Assignment را می‌گیرد؟

خیر. UUID بیشتر با شناسه Object مرتبط است و مسئله Mass Assignment درباره Propertyهای قابل تغییر است.

آیا Mass Assignment با IDOR یکسان است؟

خیر. IDOR یا BOLA معمولاً درباره دسترسی غیرمجاز به Object است، درحالی‌که Mass Assignment درباره تغییر Propertyهای غیرمجاز یک Object است.

آیا GraphQL در برابر Mass Assignment امن است؟

GraphQL می‌تواند Input Schema دقیقی ایجاد کند، اما اگر Resolver کل Input را بدون کنترل به ORM یا Domain Model منتقل کند، همچنان خطر وجود دارد.

آیا مخفی‌کردن Field در Frontend کافی است؟

خیر. تمام کنترل‌های امنیتی مرتبط با Property باید در Backend اجرا شوند.

آیا Mass Assignment می‌تواند روی Tenant Isolation تأثیر بگذارد؟

بله. Propertyهایی مانند tenantId و organizationId بسیار حساس‌اند و بهتر است معمولاً از Context سمت Server تعیین شوند، نه Request عمومی Client.

چه فیلدهایی نباید مستقیماً از Client پذیرفته شوند؟

پاسخ به معماری برنامه بستگی دارد، اما Propertyهای Role، Permission، Ownership، Tenant، Payment Status، Verification State، Audit Fields و برخی Business Statusها معمولاً باید تحت کنترل Server باشند.

آیا استفاده از DTO به‌تنهایی کافی است؟

DTO سطح حمله را به‌شدت کاهش می‌دهد، اما همچنان باید Validation، Authorization و Business Ruleهای مناسب اجرا شوند.

چگونه می‌توان فهمید Endpoint در برابر Mass Assignment مقاوم است؟

باید بررسی شود که Endpoint فقط Propertyهای مشخص‌شده در Contract را می‌پذیرد، Propertyهای امنیتی خارج از کنترل Client هستند و تست‌های منفی نیز تغییرناپذیری آن‌ها را تأیید می‌کنند.

جمع‌بندی

آسیب‌پذیری Mass Assignment زمانی ایجاد می‌شود که مرز میان داده قابل کنترل توسط Client و Propertyهای داخلی برنامه به‌درستی تعریف نشده باشد. قابلیت‌هایی مانند Auto Binding، ORM Mapping و Model Binding توسعه نرم‌افزار را سریع‌تر می‌کنند، اما اتصال مستقیم Request به Domain Model یا Database Entity می‌تواند Propertyهای حساس را ناخواسته در معرض تغییر قرار دهد.

مسئله اصلی فقط فیلترکردن چند نام مانند isAdmin یا role نیست. دفاع پایدار باید بر پایه یک معماری Default Deny ساخته شود؛ یعنی هر Endpoint فقط Propertyهایی را دریافت کند که واقعاً برای همان Use Case ضروری هستند.

استفاده از DTO و Input Model اختصاصی، Allowlist، Mapping صریح، Authorization در سطح Object و Property، Schema Validation، جداسازی عملیات مدیریتی، Response Model مستقل و تست‌های منفی از مهم‌ترین کنترل‌های دفاعی هستند.

Propertyهایی مانند Role، Ownership، Tenant، وضعیت پرداخت، وضعیت تأیید و Audit Data باید تا حد امکان توسط منطق Server تعیین شوند و مستقیماً تحت کنترل Client قرار نگیرند.

در نهایت، بهترین روش برای مقابله با Mass Assignment این است که API به‌جای انعکاس مستقیم ساختار Database، براساس عملیات واقعی کسب‌وکار طراحی شود. هرچه Contract ورودی محدودتر، Purpose-specific و صریح‌تر باشد، احتمال اینکه یک Property داخلی ناخواسته به سطح عمومی API راه پیدا کند کمتر خواهد شد.

برای تیم‌های توسعه و امنیت، بررسی Mass Assignment باید بخشی از API Security Review، Code Review، Threat Modeling و تست‌های CI/CD باشد؛ زیرا اضافه‌شدن تنها یک Property جدید به Model می‌تواند در معماری ضعیف، Endpointی را که سال‌ها بدون مشکل کار کرده است به یک نقطه آسیب‌پذیر تبدیل کند.

مطالب مرتبط