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

NoSQL Injection چیست؟ بررسی تزریق NoSQL و روش‌های جلوگیری

NoSQL Injection نوعی آسیب‌پذیری تزریق است که در آن ورودی غیرقابل‌اعتماد می‌تواند ساختار یا منطق Query پایگاه داده NoSQL را تغییر دهد. این مشکل به‌ویژه در APIهای JSON و برنامه‌هایی که Query Object یا Operatorهای کنترل‌شده توسط Client را مستقیماً به Database منتقل می‌کنند اهمیت دارد. جلوگیری از NoSQL Injection به Strict Input Validation، ساخت Query در Backend، محدودسازی Operatorها، Schema Validation و Least Privilege نیاز دارد.

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

پاسخ کوتاه: NoSQL Injection یا تزریق NoSQL نوعی آسیب‌پذیری Injection است که زمانی رخ می‌دهد که برنامه ورودی کاربر را بدون اعتبارسنجی مناسب وارد Query، Query Object یا ساختار قابل‌ارزیابی پایگاه داده NoSQL کند. پیامد آن می‌تواند دور زدن منطق برنامه، مشاهده یا تغییر اطلاعات و در برخی معماری‌ها اجرای عملیات ناخواسته باشد. دفاع اصلی شامل Input Validation، ساخت Query با فیلدهای ثابت، محدودسازی Operatorها، Least Privilege و حذف قابلیت‌های Dynamic Evaluation غیرضروری است.

پایگاه‌های داده NoSQL بخش مهمی از معماری بسیاری از برنامه‌های وب مدرن هستند. MongoDB، CouchDB، Redis، Cassandra، DynamoDB و دیگر سامانه‌های NoSQL برای کاربردهایی مانند ذخیره اسناد، Cache، داده‌های توزیع‌شده، Session، رویدادهای لحظه‌ای و سامانه‌هایی با حجم بالای اطلاعات استفاده می‌شوند.

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

این برداشت صحیح نیست.

ممکن است Syntax و مدل Query تغییر کند، اما اصل مسئله همچنان پابرجاست:

اگر داده‌ای که مهاجم کنترل می‌کند بتواند ساختار یا منطق Query را تغییر دهد، Injection همچنان امکان‌پذیر است.

در SQL Injection، مهاجم معمولاً تلاش می‌کند ورودی را از محدوده Data خارج و وارد ساختار دستور SQL کند. در NoSQL Injection، مهاجم ممکن است به جای تغییر یک رشته SQL، ساختار JSON یا BSON، Query Operator، Expression، Filter Object یا حتی کد قابل اجرا توسط Database Engine را تحت تأثیر قرار دهد.

OWASP نیز NoSQL Injection را یکی از Failure Modeهای مهم در امنیت NoSQL معرفی می‌کند و نسبت به ساخت Query Object یا Query String از ورودی غیرقابل‌اعتماد هشدار می‌دهد.

برای توسعه‌دهندگان و مدیران سایت، اهمیت موضوع زمانی بیشتر می‌شود که Backend با JavaScript یا Node.js نوشته شده باشد؛ زیرا داده‌های JSON دریافتی از HTTP Request می‌توانند شباهت زیادی به ساختار Query MongoDB داشته باشند. اگر برنامه این داده را بدون محدودسازی وارد Query کند، مرز میان «داده کاربر» و «دستور پایگاه داده» از بین می‌رود.

در این مقاله از رخنه‌کاو بررسی می‌کنیم NoSQL Injection چیست، چگونه به وجود می‌آید، چه تفاوتی با SQL Injection دارد، چه انواعی از تزریق NoSQL وجود دارد، چرا MongoDB معمولاً در مثال‌های امنیتی مطرح می‌شود و چگونه می‌توان با طراحی امن، Input Validation، Schema Validation، RBAC، Least Privilege و مانیتورینگ مناسب ریسک این آسیب‌پذیری را کاهش داد.

NoSQL چیست؟

NoSQL اصطلاحی عمومی برای گروهی از پایگاه‌های داده است که الزاماً از مدل رابطه‌ای و SQL سنتی استفاده نمی‌کنند.

عبارت NoSQL را معمولاً به معنی Not Only SQL در نظر می‌گیرند؛ یعنی این سامانه‌ها الزاماً جایگزین مطلق SQL نیستند، بلکه مدل‌های دیگری برای ذخیره و Query داده فراهم می‌کنند.

پایگاه‌های NoSQL بسته به نوع طراحی می‌توانند داده را در مدل‌های مختلف نگهداری کنند.

برای مثال:

Document Databaseها مانند MongoDB داده‌ها را به شکل Documentهای مشابه JSON ذخیره می‌کنند.

Key-Value Databaseها مانند Redis بیشتر روی ساختار Key و Value تمرکز دارند.

Wide-Column Databaseها مانند Cassandra برای حجم زیاد داده و معماری توزیع‌شده طراحی شده‌اند.

Graph Databaseها داده و روابط میان آن‌ها را به شکل گراف مدل می‌کنند.

هرکدام از این سامانه‌ها زبان Query، API و مدل امنیتی خاص خود را دارند.

به همین دلیل NoSQL Injection یک Payload یا Syntax جهانی ندارد. OWASP نیز تأکید می‌کند که صدها پایگاه NoSQL با APIها و مدل‌های مختلف وجود دارند و یک نمونه Injection واحد را نمی‌توان برای همه آن‌ها تعمیم داد.

همین تفاوت یکی از مهم‌ترین نکات برای تیم امنیت است.

در SQL Injection معمولاً با ساختار SQL روبه‌رو هستیم؛ اما در NoSQL ممکن است نقطه آسیب‌پذیر در:

JSON

BSON

Query Object

JavaScript Expression

REST API

Graph Query

Search Expression

یا Library اختصاصی Database

قرار داشته باشد.

NoSQL Injection چیست؟

NoSQL Injection زمانی شکل می‌گیرد که برنامه اجازه دهد Input کنترل‌شده توسط کاربر در بخشی از Query قرار بگیرد که می‌تواند منطق یا ساختار Query را تغییر دهد.

برای مثال تصور کنید API قرار است فقط یک Username را از کاربر دریافت کند.

برنامه انتظار دارد:

{
  "username": "aria"
}

اما به جای آنکه Backend مطمئن شود مقدار username واقعاً یک String است، کل ساختار دریافتی را بدون محدودسازی وارد Query کند.

در این شرایط ممکن است API علاوه بر مقدار مورد انتظار، ساختارهای Query را نیز بپذیرد.

مشکل اصلی در اینجا «MongoDB» یا «JSON» نیست.

مشکل از بین رفتن مرز میان Data و Query Logic است.

یک الگوی خطرناک در Node.js ممکن است از نظر مفهومی چنین باشد:

const filter = req.body;

const user = await db
  .collection("users")
  .findOne(filter);

در این نمونه، Client عملاً بخشی از Query Object را تعیین می‌کند.

برنامه به جای اینکه خودش Query را بسازد، Query را از کاربر دریافت کرده است.

راه امن‌تر این است که Backend فقط مقادیر مشخصی را استخراج و نوع آن‌ها را کنترل کند:

const username = String(req.body.username ?? "");

const user = await db
  .collection("users")
  .findOne({
    username: username
  });

در معماری‌های حرفه‌ای معمولاً Validation دقیق‌تری نیز روی طول، فرمت و محدوده مقدار انجام می‌شود.

تفاوت این دو طراحی بسیار مهم است.

در حالت اول:

Client → Query Structure

اما در حالت دوم:

Client → Data → Validation → Query ثابت

هدف Secure Coding این است که کاربر بتواند داده را کنترل کند، نه منطق Query را.

چرا پایگاه NoSQL هم در برابر Injection آسیب‌پذیر است؟

گاهی گفته می‌شود MongoDB SQL ندارد، بنابراین SQL Injection در آن وجود ندارد.

این جمله تا حدی درست اما گمراه‌کننده است.

MongoDB Queryهای معمولی را به شکل BSON Object پردازش می‌کند و تزریق رشته‌ای سنتی SQL لزوماً روی آن قابل اجرا نیست. مستندات رسمی MongoDB توضیح می‌دهد که Queryهای معمولی با BSON ساخته می‌شوند و بسیاری از مشکلات String-Based SQL Injection به همان شکل در این مدل وجود ندارند.

اما از این موضوع نمی‌توان نتیجه گرفت که هر Query MongoDB امن است.

اگر برنامه:

Query Object خام را از Client بپذیرد،

اجازه استفاده از Operatorهای کنترل‌شده توسط Client را بدهد،

داده را وارد JavaScript Server-Side کند،

Query Expression را با String Concatenation بسازد،

یا Dynamic Evaluation انجام دهد،

NoSQL Injection می‌تواند شکل بگیرد.

بنابراین باید بین دو مفهوم تفاوت قائل شد:

Database API امن

و

استفاده امن از Database API

وجود یک Driver امن نمی‌تواند طراحی اشتباه Application را جبران کند. تفاوت SQL Injection و NoSQL Injection

تفاوت SQL Injection و NoSQL Injection

SQL Injection و NoSQL Injection هر دو زیرمجموعه خانواده Injection هستند، اما نحوه ایجاد آن‌ها می‌تواند متفاوت باشد.

ویژگیSQL InjectionNoSQL Injection
هدف معمولSQL QueryQuery Object، JSON، BSON یا Expression
مدل دادهعمدتاً RelationalDocument، Key-Value، Graph و غیره
روش رایج ایجادString ConcatenationQuery Object ناامن یا Dynamic Evaluation
ورودی خطرناکSQL SyntaxOperator، Object، Expression یا Query Structure
دفاع اصلیParameterized QueryStructured Query + Type Validation
خطر Dynamic Codeبسته به DBMSدر برخی NoSQLها بسیار مهم
Schemaمعمولاً سخت‌گیرانه‌تردر برخی سیستم‌ها Flexible
کنترل نوعمعمولاً اهمیت دارددر JSON APIها اهمیت بسیار زیادی دارد

نکته مهم این است که در NoSQL Injection همیشه لازم نیست یک String شکسته شود.

گاهی کافی است برنامه به جای یک String، Object دریافت کند و همان Object را مستقیماً به Database Driver تحویل دهد.

به همین دلیل Type Validation در APIهای NoSQL اهمیت بسیار زیادی پیدا می‌کند. NoSQL Injection چگونه ایجاد می‌شود؟

NoSQL Injection چگونه ایجاد می‌شود؟

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

فرض کنید Endpoint ورود برنامه اطلاعاتی مانند این دریافت می‌کند:

{
  "username": "user",
  "password": "value"
}

Backend باید مطمئن شود:

username یک String است.

password یک String است.

حداکثر طول هر دو مشخص است.

فیلد اضافی وجود ندارد.

Object تو در تو به جای String پذیرفته نمی‌شود.

Query Operator از Client دریافت نمی‌شود.

اما اگر برنامه Request Body را بدون Schema Validation مستقیماً وارد Filter کند، ساختار Query دیگر کاملاً تحت کنترل Application نیست.

به همین دلیل Validation فقط به معنی حذف < یا ' نیست.

OWASP در راهنمای تست NoSQL Injection اشاره می‌کند که فیلترهایی که صرفاً Characterهای رایج HTML یا SQL را حذف می‌کنند، لزوماً در برابر APIهای JSON مؤثر نیستند؛ زیرا Syntax و ساختار خطر در آن‌ها متفاوت است. Operator Injection چیست؟

Operator Injection چیست؟

یکی از مهم‌ترین انواع NoSQL Injection را می‌توان Operator Injection دانست.

در این حالت ورودی کاربر به جای آنکه فقط یک Value باشد، تبدیل به بخشی از Query Logic می‌شود.

Databaseهای Document-Based معمولاً Operatorهایی برای:

مقایسه،

فیلتر،

Regex،

ترکیب شرط‌ها،

بررسی وجود فیلد،

و سایر عملیات Query

دارند.

خود این Operatorها آسیب‌پذیری نیستند.

آن‌ها قابلیت طبیعی پایگاه داده هستند.

مشکل زمانی ایجاد می‌شود که Client بتواند Operator دلخواه خود را مستقیماً وارد Query کند، در حالی که Backend انتظار دارد فقط مقدار داده دریافت کند.

برای جلوگیری از این وضعیت، API نباید Query Language پایگاه داده را مستقیماً به اینترنت Export کند مگر آنکه محصول عمداً یک Query API باشد و کنترل‌های بسیار دقیق روی آن اعمال شود.

برای اکثر وب‌سایت‌ها و Applicationها بهتر است API یک Contract محدود و مشخص داشته باشد.

برای مثال:

GET /users?name=aria

Backend باید خودش تصمیم بگیرد چگونه این مقدار به Query تبدیل شود.

Client نباید بتواند Query MongoDB را طراحی کند.

JSON Injection و تغییر ساختار داده

یکی از خصوصیات APIهای مدرن آن است که Request Body معمولاً JSON است.

JSON از نظر ظاهری بسیار ساده است، اما می‌تواند:

String

Number

Boolean

Array

Object

Null

را منتقل کند.

اگر Backend فرض کند هر فیلد همیشه String است، اما در عمل Parser اجازه Object یا Array نیز بدهد، ممکن است رفتار Query تغییر کند.

برای مثال Application انتظار دارد:

{
  "email": "[email protected]"
}

اما اگر هیچ Type Validation وجود نداشته باشد، ساختاری مانند زیر از نظر Parser همچنان JSON معتبر است:

{
  "email": {
    "unexpected": "object"
  }
}

این مثال یک Exploit نیست، اما مسئله را نشان می‌دهد:

JSON معتبر الزاماً Input معتبر برای Application نیست.

Backend باید Contract API را اجرا کند.

اگر Email باید String باشد، هر نوع دیگری باید Reject شود.

بهتر است Validation قبل از رسیدن داده به Database Layer انجام شود. JavaScript Injection در پایگاه‌های NoSQL

JavaScript Injection در پایگاه‌های NoSQL

برخی Databaseها قابلیت اجرای JavaScript یا Expressionهای Dynamic را ارائه می‌کنند.

MongoDB historically امکاناتی مانند $where و برخی Server-Side JavaScript Functionها داشته است.

خطر زمانی ایجاد می‌شود که Application ورودی User را داخل JavaScript Expression قرار دهد.

مستندات رسمی MongoDB توصیه می‌کند از Concatenation یا Interpolation ورودی غیرقابل‌اعتماد در $where، $function یا $accumulator خودداری شود و در صورت امکان از Operatorهای استاندارد Query استفاده شود.

از MongoDB 8.0 نیز قابلیت‌های Server-Side JavaScript شامل $where، $function و $accumulator Deprecated شده‌اند و MongoDB هنگام استفاده از آن‌ها Warning ثبت می‌کند.

این تغییر جهت مهمی است.

در طراحی جدید بهتر است تا جای ممکن Query با Operatorهای معمول Database انجام شود، نه JavaScript Dynamic.

چرا $where حساس است؟

Operator $where به MongoDB اجازه می‌دهد JavaScript Expression را در فرایند Query ارزیابی کند.

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

MongoDB نیز در مستندات خود توضیح می‌دهد که $where نمی‌تواند مانند Queryهای عادی از Indexها استفاده کند و معمولاً بهتر است از Operatorهای استاندارد استفاده شود.

از دید امنیتی نیز اگر User Input وارد Expression شود، مرز Data و Executable Code ضعیف می‌شود.

قاعده امن ساده است:

ورودی کاربر را داخل JavaScript قابل اجرا قرار ندهید.

اگر برنامه اصلاً به Server-Side JavaScript احتیاج ندارد، می‌توان آن را در MongoDB غیرفعال کرد.

MongoDB برای Self-Managed Deployment امکان استفاده از:

security.javascriptEnabled: false

یا گزینه:

--noscripting

را فراهم می‌کند.

غیرفعال‌سازی Feature غیرضروری نمونه‌ای از اصل Reduction of Attack Surface است.

NoSQL Injection در Authentication

Login Endpoint یکی از حساس‌ترین بخش‌هایی است که باید از نظر NoSQL Injection بررسی شود.

علت روشن است:

Query معمولاً مستقیماً تعیین می‌کند آیا User معتبر وجود دارد یا خیر.

یک طراحی نامناسب ممکن است چیزی شبیه این داشته باشد:

const user = await users.findOne({
  username: req.body.username,
  password: req.body.password
});

این نمونه علاوه بر مشکل واضح ذخیره یا Query مستقیم Password، یک مسئله دیگر نیز دارد:

هیچ تضمینی وجود ندارد که username و password String باشند.

طراحی امن‌تر ابتدا Contract ورودی را بررسی می‌کند:

if (
  typeof req.body.username !== "string" ||
  typeof req.body.password !== "string"
) {
  return res.status(400).end();
}

سپس Password باید براساس روش امن Hashing بررسی شود؛ نه آنکه Password خام مستقیماً بخشی از Database Query باشد.

معمولاً Workflow بهتر چنین است:

Username معتبر دریافت شود.

User براساس Username جستجو شود.

Password Hash ذخیره‌شده دریافت شود.

Password با الگوریتم Password Hashing مناسب Verify شود.

نتیجه احراز هویت مستقل از ساختار Query پردازش شود.

این معماری سطح Attack Surface مربوط به Query را کاهش می‌دهد.

چرا ذخیره Password خام مشکل امنیتی جداگانه‌ای است؟

اگر برنامه Password را مستقیماً داخل Database Query مقایسه کند، معمولاً این احتمال وجود دارد که Passwordها به شکل نامناسب ذخیره شده باشند.

Password نباید به‌صورت Plaintext ذخیره شود.

بهتر است از Password Hashing Algorithm طراحی‌شده برای رمز عبور، با تنظیمات مناسب و Salt استفاده شود.

این مسئله مستقیماً NoSQL Injection نیست، اما در Endpointهای Authentication معمولاً این دو موضوع به یکدیگر نزدیک هستند.

Secure Design یعنی یک ضعف نتواند بلافاصله به افشای Credentialهای قابل استفاده منجر شود.

Blind NoSQL Injection چیست؟

در بعضی شرایط Vulnerability وجود دارد اما Application نتیجه Query را مستقیماً نمایش نمی‌دهد.

ممکن است مهاجم فقط بتواند تفاوت رفتار سیستم را مشاهده کند.

برای مثال:

تغییر در پاسخ موفق یا ناموفق،

تفاوت Error،

تفاوت تعداد Result،

یا تفاوت قابل توجه در رفتار درخواست.

به این دسته از سناریوها در ادبیات امنیتی معمولاً Blind یا Inferential Injection گفته می‌شود.

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

از دید دفاعی نکته مهم این است که:

حتی اگر Database Error نمایش داده نشود و Query Result مستقیماً به کاربر نشان داده نشود، NoSQL Injection همچنان ممکن است وجود داشته باشد.

به همین دلیل Code Review و بررسی Data Flow اهمیت زیادی دارد.

Error-Based NoSQL Injection

یکی دیگر از نشانه‌های طراحی ضعیف، نمایش Errorهای داخلی Database به User است.

فرض کنید Input غیرمنتظره باعث Error Parser یا Database Driver شود.

اگر Application همان Error را بدون کنترل نمایش دهد، ممکن است اطلاعاتی مانند:

نام Collection،

نام Field،

نوع Query،

Database Driver،

Stack Trace،

File Path،

یا نسخه Library

افشا شود.

این اطلاعات می‌توانند برای Reconnaissance مفید باشند.

در Production بهتر است Client فقط یک Error کنترل‌شده دریافت کند.

جزئیات فنی باید در Server Log ثبت شوند.

برای مثال:

{
  "error": "Invalid request"
}

اما Server می‌تواند Event دقیق را همراه Correlation ID ثبت کند.

Regex و Queryهای پرهزینه

NoSQL Injection همیشه فقط به دسترسی غیرمجاز منجر نمی‌شود.

گاهی Query کنترل‌نشده می‌تواند مشکل Performance یا Denial of Service ایجاد کند.

برای مثال اگر Application اجازه دهد User Pattern جستجوی بسیار پیچیده یا Query بسیار گسترده ایجاد کند، CPU یا Memory Database ممکن است تحت فشار قرار گیرد.

بنابراین Validation باید علاوه بر امنیت منطقی، Cost Query را نیز در نظر بگیرد.

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

حداکثر طول Search Term،

حداکثر تعداد Filter،

Pagination اجباری،

Timeout،

محدودیت Sort،

محدودیت Regex،

و Query Complexity

می‌توانند اهمیت داشته باشند.

در APIهای عمومی نباید اجازه داد User هر Query دلخواهی روی Database اجرا کند.

Mass Assignment چه تفاوتی با NoSQL Injection دارد؟

Mass Assignment و NoSQL Injection می‌توانند ظاهراً مشابه باشند، اما یکسان نیستند.

فرض کنید Application چنین کاری انجام دهد:

await users.updateOne(
  { _id: userId },
  { $set: req.body }
);

اگر req.body شامل Fieldهایی باشد که User نباید تغییر دهد، ممکن است Mass Assignment رخ دهد.

برای مثال Client شاید بتواند Fieldهای داخلی، Role یا وضعیت حساب را ارسال کند.

NoSQL Injection بیشتر درباره تغییر Query Logic یا Database Operation است.

Mass Assignment درباره کنترل Fieldهای بیش از حد مجاز است.

اما ریشه مشترک هر دو مشکل اغلب یکی است:

اعتماد بیش از حد به Object دریافتی از Client.

راهکار مشترک نیز Allowlist است.

Backend باید Fieldهای مجاز را خودش انتخاب کند:

const update = {
  displayName: req.body.displayName,
  bio: req.body.bio
};

نه اینکه کل Object را ذخیره کند.

Prototype Pollution و NoSQL Injection

در برنامه‌های JavaScript گاهی Prototype Pollution نیز می‌تواند با Data Processing یا Query Construction تداخل داشته باشد.

این دو Vulnerability یکسان نیستند، اما Object Merge ناامن یا Libraryهای آسیب‌پذیر ممکن است باعث شوند Propertyهایی به Objectهای داخلی اضافه شوند.

اگر Application از Objectهای کنترل‌نشده برای Query یا Authorization استفاده کند، اثر ترکیبی می‌تواند جدی شود.

برای کاهش ریسک:

از Dependencyهای به‌روز استفاده کنید.

Object Mergeهای غیرضروری را حذف کنید.

ورودی JSON را Schema Validate کنید.

Propertyهای ناشناخته را Reject کنید.

از Objectهای Client به‌عنوان Security Context استفاده نکنید.

NoSQL Injection در Node.js و Express

ترکیب Node.js، Express و MongoDB یکی از معماری‌های رایج وب است.

در چنین برنامه‌هایی Request Body اغلب پس از JSON Parsing مستقیماً به JavaScript Object تبدیل می‌شود.

مثلاً:

req.body

ممکن است Object کاملی باشد.

اگر Developer این Object را مستقیماً به:

findOne()

find()

updateOne()

deleteOne()

یا Aggregation Pipeline

تحویل دهد، Client ممکن است بیش از آنچه Developer تصور کرده روی Query تأثیر داشته باشد.

اصل امن این است که Controller ورودی را به DTO یا Schema مشخص تبدیل کند.

برای مثال:

const input = {
  email: String(req.body.email ?? "").trim()
};

سپس Validation انجام شود.

بعد Service Layer Query را خودش بسازد.

این جداسازی باعث می‌شود HTTP Request هیچ‌گاه مستقیماً Database Query نباشد.

آیا ODM یا ORM جلوی NoSQL Injection را می‌گیرد؟

ODMهایی مانند Mongoose می‌توانند Validation، Schema و Type Casting فراهم کنند و احتمال برخی خطاها را کاهش دهند.

اما وجود ODM به معنی امنیت خودکار نیست.

اگر Developer:

Raw Query اجرا کند،

Filter خام از Client بپذیرد،

Operatorهای Client-Controlled را وارد Query کند،

یا Validation را غیرفعال کند،

آسیب‌پذیری همچنان ممکن است ایجاد شود.

بنابراین هیچ Framework یا Library نباید به‌عنوان جایگزین Secure Design در نظر گرفته شود.

Dependency ابزار است؛ Trust Boundary را Application تعریف می‌کند.

مهم‌ترین اصل: هر ورودی را Untrusted بدانید

OWASP در NoSQL Security Cheat Sheet یکی از اصول Secure-by-Design را Treat All Input as Untrusted معرفی می‌کند.

Untrusted Input فقط Form Input نیست.

موارد زیر همگی ممکن است تحت کنترل User یا سیستم خارجی باشند:

Request Body

Query Parameter

URL Parameter

HTTP Header

Cookie

WebSocket Message

Uploaded File Metadata

Webhook

Message Queue

Third-Party API

Mobile Application Request

GraphQL Variable

و حتی داده‌ای که قبلاً در Database ذخیره شده است.

داده Stored نیز ممکن است در گذشته بدون Validation ذخیره شده باشد.

بنابراین اعتماد نباید صرفاً براساس محل دریافت Input تعریف شود.

Input Validation؛ اولین خط دفاع

Validation باید براساس Allowlist انجام شود.

یعنی Application دقیقاً تعریف کند چه چیزی معتبر است.

برای مثال Username:

نوع: String

حداقل طول: 3

حداکثر طول: 40

Character Set: براساس نیاز محصول

Object: غیرمجاز

Array: غیرمجاز

Null: غیرمجاز

Unknown Field: غیرمجاز

یک Validation ساده در JavaScript:

function isValidUsername(value) {
  return (
    typeof value === "string" &&
    value.length >= 3 &&
    value.length <= 40
  );
}

در Production بهتر است از Schema Validation Library قابل اعتماد استفاده شود تا Contractها در همه Endpointها یکسان اجرا شوند.

Type Validation چرا اهمیت ویژه دارد؟

در برنامه SQLمحور معمولاً Developer بیشتر نگران Characterهایی مانند Quote است.

اما در JSON API باید سؤال مهم‌تری پرسید:

نوع داده چیست؟

اگر Application انتظار String دارد، Object نباید پذیرفته شود.

اگر Number انتظار می‌رود، "100" و 100 الزاماً نباید یکسان تلقی شوند.

اگر Boolean نیاز است، String "false" نباید به شکل اشتباه Truthy شود.

اگر Array پذیرفته می‌شود، تعداد Element باید محدود باشد.

Strict Type Validation یکی از مؤثرترین کنترل‌ها در NoSQL Applicationهاست.

استفاده از JSON Schema

برای APIهای پیچیده می‌توان از JSON Schema یا Validation Libraryهای مشابه استفاده کرد.

Schema می‌تواند موارد زیر را مشخص کند:

Type

Required Field

Minimum و Maximum

Length

Pattern

Allowed Value

Additional Properties

Nested Structure

Array Size

برای مثال یک Contract می‌تواند اعلام کند:

فقط email و password مجازند.

هر دو باید String باشند.

هیچ Property اضافی پذیرفته نمی‌شود.

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

Schema Validation در خود MongoDB

علاوه بر Validation در Application، MongoDB نیز Schema Validation ارائه می‌دهد.

MongoDB Schema Validation می‌تواند برای Fieldها مواردی مانند Data Type و محدوده Value تعریف کند و به‌طور پیش‌فرض Insert یا Updateای را که Ruleها را نقض کند Reject کند.

این قابلیت جای Application Validation را نمی‌گیرد.

بهتر است Defense in Depth داشته باشیم:

Client Validation

↓

API Validation

↓

Business Logic Validation

↓

Database Schema Validation

Client Validation بیشتر برای UX است.

امنیت اصلی باید در Server برقرار باشد.

Database Validation نیز آخرین لایه‌ای است که از ذخیره داده نامعتبر جلوگیری می‌کند.

Query را خود Application بسازد

یکی از بهترین راهکارهای جلوگیری از NoSQL Injection این است که Query Structure در Code ثابت باشد.

الگوی مناسب:

const email = validateEmail(req.body.email);

const user = await users.findOne({
  email: email
});

الگوی نامناسب:

const filter = req.body.filter;

const user = await users.findOne(filter);

مگر اینکه محصول عمداً Query Builder عمومی ارائه دهد و سیستم Policy بسیار دقیق روی Filterها داشته باشد.

برای Application عادی، Client نباید بتواند Database Operator انتخاب کند.

Allowlist بهتر از Blocklist است

ممکن است تیم توسعه تصمیم بگیرد Character یا Operatorهای خطرناک را Block کند.

این رویکرد معمولاً شکننده است.

Database Query Language می‌تواند پیچیده باشد.

Encoding، Nested Object، Library Behavior و نسخه‌های مختلف ممکن است راه‌های جدیدی ایجاد کنند.

بهتر است به جای پرسش:

«چه چیزهایی را ممنوع کنیم؟»

بپرسیم:

«دقیقاً چه چیزی را نیاز داریم؟»

اگر Endpoint فقط Email می‌خواهد، فقط یک Email String معتبر پذیرفته شود.

هیچ دلیل منطقی برای پذیرفتن Object عمومی وجود ندارد.

Operatorهای Client-Controlled را محدود کنید

بعضی APIها واقعاً به Filterهای پیشرفته نیاز دارند.

برای مثال Dashboard مدیریتی ممکن است امکان فیلتر براساس:

تاریخ،

وضعیت،

نوع،

و محدوده قیمت

را ارائه دهد.

در چنین شرایطی نباید Client مستقیماً MongoDB Query ارسال کند.

یک DSL محدود تعریف کنید.

مثلاً Client بگوید:

{
  "status": "active",
  "sort": "created_at",
  "direction": "desc"
}

Backend این Contract را Validation می‌کند و سپس خودش آن را به Query تبدیل می‌کند.

این معماری یک Translation Layer ایجاد می‌کند:

Public API Filter

↓

Validation

↓

Internal Query Builder

↓

MongoDB

این طراحی بسیار امن‌تر از Export کردن Query Language Database است.

Raw Query را از اینترنت نپذیرید

یکی از مهم‌ترین توصیه‌های OWASP در NoSQL Security این است که Raw JSON Query از Client پذیرفته نشود و Queryهای خام یا Eval روی Input غیرقابل‌اعتماد اجرا نشوند.

Endpointی مانند:

POST /search

نباید Bodyای با معنای:

«هر Query MongoDB که خواستی ارسال کن»

را برای User عادی فراهم کند.

اگر چنین قابلیت پیشرفته‌ای برای Admin Tool لازم است، باید:

Network محدود باشد.

Strong Authentication اعمال شود.

Authorization دقیق باشد.

Query Capability محدود شود.

Audit Log وجود داشته باشد.

Rate Limit فعال باشد.

و Scope داده کنترل شود.

اصل Least Privilege

فرض کنیم باوجود تمام Validationها یک NoSQL Injection باقی مانده است.

سؤال بعدی این است:

Database Account برنامه چه مجوزهایی دارد؟

اگر Application با Account دارای دسترسی کامل Administrator به Database متصل شود، اثر Vulnerability بسیار شدیدتر خواهد بود.

اما اگر Service Account فقط به Collectionهای مورد نیاز و Operationهای لازم دسترسی داشته باشد، دامنه آسیب محدودتر می‌شود.

این اصل Least Privilege است.

MongoDB از Role-Based Access Control یا RBAC پشتیبانی می‌کند و Roleها مشخص می‌کنند User چه Actionهایی روی چه Resourceهایی انجام دهد.

برای مثال Frontend APIای که فقط اطلاعات عمومی Product را می‌خواند، معمولاً نیازی به:

ساخت User،

حذف Database،

تغییر Role،

یا دسترسی به Collectionهای داخلی

ندارد.

برای هر سرویس Credential جداگانه داشته باشید

استفاده از یک Database Credential مشترک برای تمام Microserviceها طراحی مناسبی نیست.

برای مثال:

Auth Service

Product Service

Analytics Service

Admin Service

بهتر است Identity و Permission جداگانه داشته باشند.

MongoDB نیز در راهنمای User Management توصیه می‌کند Applicationها و Userهای مختلف به User مجزا نگاشت شوند و فقط حداقل Privilege لازم دریافت کنند.

مزایای این کار شامل:

کاهش Blast Radius،

امکان Revocation مستقل،

Audit بهتر،

و تشخیص دقیق‌تر Incident

است.

دسترسی Database را مستقیماً به اینترنت باز نکنید

حتی اگر Application امن نوشته شده باشد، Database Port نباید بدون ضرورت در معرض Public Internet قرار گیرد.

OWASP در NoSQL Security Checklist توصیه می‌کند Management Interface و Database Port محدود شوند و Database روی شبکه خصوصی یا Internal IP قرار گیرد.

معماری مناسب:

Internet
   ↓
Reverse Proxy / WAF
   ↓
Application
   ↓
Private Network
   ↓
Database

نه:

Internet
   ↓
Database

Network Segmentation جای Secure Coding را نمی‌گیرد، اما یک لایه دفاعی مهم است.

TLS برای ارتباط Database

ارتباط Application و Database ممکن است شامل:

اطلاعات شخصی،

Token،

Session،

Credential،

اطلاعات مالی،

و سایر داده‌های حساس باشد.

در محیط Production باید براساس Threat Model و معماری از Encryption in Transit استفاده شود.

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

TLS از NoSQL Injection جلوگیری نمی‌کند، اما مانع دیگری در برابر شنود و تغییر Traffic ایجاد می‌کند.

امنیت Database مجموعه‌ای از کنترل‌هاست، نه فقط Injection Prevention.

Secretها را داخل Source Code نگه ندارید

Database Connection String نباید در Repository عمومی یا Source Code قرار گیرد.

Credential بهتر است از:

Secret Manager

Environment Secret

Vault

یا سامانه مدیریت Credential

دریافت شود.

همچنین Secretها باید:

قابل Rotation باشند.

Scope محدود داشته باشند.

در Log چاپ نشوند.

در Error Message ظاهر نشوند.

و برای Development و Production یکسان نباشند.

اگر NoSQL Injection با Credential دارای Privilege زیاد ترکیب شود، Impact می‌تواند بسیار بیشتر شود.

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

Sanitization می‌تواند مفید باشد، اما نباید کنترل اصلی باشد.

دو مفهوم مهم وجود دارد:

Validation

و

Sanitization

Validation می‌پرسد:

«آیا این Input همان چیزی است که Application انتظار دارد؟»

Sanitization می‌پرسد:

«آیا می‌توان Input را تغییر داد تا امن‌تر شود؟»

برای بسیاری از Security Boundaryها بهتر است Input نامعتبر Reject شود.

برای مثال اگر username باید String باشد اما Object دریافت شده، بهتر است Request رد شود.

تبدیل Object ناشناخته به String ممکن است رفتار غیرمنتظره‌ای ایجاد کند.

Error Handling امن

Database Error نباید مستقیماً برای User ارسال شود.

این کد:

catch (err) {
  res.status(500).send(err);
}

در Production مناسب نیست.

راه بهتر:

catch (err) {
  logger.error({
    event: "database_query_failed",
    requestId
  });

  res.status(500).json({
    error: "Internal server error"
  });
}

البته Logger نیز نباید Secret یا Full Request Body حساس را بدون Redaction ذخیره کند.

Logging برای شناسایی NoSQL Injection

مانیتورینگ می‌تواند تلاش‌های غیرعادی را آشکار کند.

Eventهای مهم عبارت‌اند از:

Validation Failure زیاد

Object به جای String

Unknown Field

Query Operator غیرمجاز

Database Parser Error

افزایش 400 یا 422

تعداد زیاد Login Failure

Queryهای پرهزینه

Execution Time غیرعادی

Requestهای مشابه از یک IP

افزایش Errorهای Database

استفاده از Endpointهای Query پیشرفته

Logging باید به شکلی انجام شود که داده حساس افشا نشود.

Password، Token، Session Cookie و Secret نباید در Log ذخیره شوند.

Rate Limiting

Rate Limiting NoSQL Injection را اصلاح نمی‌کند، اما هزینه Exploitation و Automated Probing را افزایش می‌دهد.

Endpointهای حساس مانند:

Login

Password Reset

Search پیچیده

Admin API

Export

و Filter API

می‌توانند Rate Limit اختصاصی داشته باشند.

Rate Limiting بهتر است فقط براساس IP نباشد.

در صورت امکان ترکیبی از:

IP

Account

Session

API Key

و رفتار

در نظر گرفته شود.

جلوگیری از Queryهای بسیار پرهزینه

حتی Query مجاز ممکن است از نظر Performance خطرناک باشد.

برای APIهای Search موارد زیر را محدود کنید:

تعداد Result

Page Size

Aggregation Stage

Sort Field

Filter Count

Regex Complexity

Query Time

Export Size

Nested Depth

همچنین Index مناسب باعث می‌شود Queryهای قانونی هزینه کمتری داشته باشند.

Rate Limit و Query Cost Limit مکمل یکدیگرند.

Web Application Firewall و NoSQL Injection

WAF می‌تواند برخی Patternهای شناخته‌شده را شناسایی کند، اما نباید دفاع اصلی باشد.

دلیل آن این است که NoSQL Query ممکن است داخل:

JSON Body

Nested Object

GraphQL Variable

Encoded Payload

یا Protocol دیگری

قرار داشته باشد.

علاوه بر این، Syntax میان Databaseهای NoSQL متفاوت است.

WAF یک Defense-in-Depth Control است.

کنترل اصلی باید در Application باشد:

Schema Validation

Query Construction امن

Authorization

و Least Privilege.

NoSQL Injection در GraphQL

GraphQL به خودی خود NoSQL Injection ایجاد نمی‌کند.

مشکل زمانی ایجاد می‌شود که Resolver داده ورودی را مستقیماً به Database Query تبدیل کند.

برای مثال Filter Object در GraphQL نباید بدون Translation و Validation مستقیماً تبدیل به Query MongoDB شود.

Resolver باید مشخص کند:

چه Fieldهایی قابل Filter هستند؟

چه Operatorهایی وجود دارند؟

حداکثر Depth چقدر است؟

چه Permissionهایی نیاز است؟

آیا User به همان Resource دسترسی دارد؟

GraphQL Schema بخشی از Contract است، اما Database Query Boundary نیز باید جداگانه کنترل شود.

NoSQL Injection در REST API

REST APIها معمولاً بیشتر در معرض Query Parameter و JSON Body هستند.

برای مثال Endpoint:

GET /products

ممکن است Filter دریافت کند.

به جای قبول Filter خام، Parameterهای محدود طراحی کنید:

category
min_price
max_price
page
sort

هر Parameter مستقل Validation شود.

سپس Query Builder داخلی آن‌ها را به Query Database تبدیل کند.

این کار Public API را از Database Syntax جدا می‌کند.

امنیت Update و Delete

بحث NoSQL Injection فقط find() نیست.

Update و Delete بسیار حساس‌تر هستند.

اگر User بتواند Filter مربوط به Update را کنترل کند، ممکن است Scope تغییر ناخواسته‌ای پیدا کند.

Backend باید:

Target Resource را خودش تعیین کند.

Ownership را بررسی کند.

Authorization انجام دهد.

Fieldهای Update را Allowlist کند.

و نتیجه Operation را بررسی کند.

برای مثال User نباید صرفاً با فرستادن Document ID بتواند Object متعلق به User دیگری را ویرایش کند.

این مشکل ممکن است همزمان با IDOR یا Broken Object Level Authorization رخ دهد.

Authentication و Authorization همچنان ضروری‌اند

NoSQL Database قرار نیست جای Application Authorization را بگیرد.

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

User چه کسی است؟

چه Roleای دارد؟

به کدام Tenant تعلق دارد؟

مالک Resource کیست؟

آیا Action مجاز است؟

اگر Query Injection امکان تغییر Filter را ایجاد کند اما Application Authorization در Layer دیگری نیز برقرار باشد، ممکن است Impact محدود شود.

این همان Defense in Depth است.

Multi-Tenant Applicationها

NoSQL Injection در SaaSهای Multi-Tenant خطر ویژه‌ای دارد.

فرض کنید هر Document دارای:

tenantId

است.

Queryها باید همواره Scope Tenant را رعایت کنند.

اگر Developer اجازه دهد User کل Filter را کنترل کند، احتمال حذف یا تغییر Tenant Scope وجود دارد.

الگوی مناسب این است که Security Constraint توسط Server اضافه شود:

const filter = {
  tenantId: auth.tenantId,
  status: validatedStatus
};

tenantId نباید از Request Body عادی گرفته شود.

Identity و Tenant Context باید از Authentication Context استخراج شوند.

Database Schema انعطاف‌پذیر یعنی بدون Rule نیست

یکی از مزایای NoSQL Flexible Schema است.

اما Flexible Schema نباید به معنی:

«هر چیزی قابل ذخیره است»

تفسیر شود.

پس از تثبیت Domain Model بهتر است Validation Rule تعریف شود.

MongoDB Schema Validation به‌طور مشخص برای جلوگیری از Data Type اشتباه و Schema Change ناخواسته طراحی شده است.

برای مثال:

email باید String باشد.

role فقط از مجموعه مشخصی باشد.

createdAt باید Date باشد.

balance باید Number باشد.

اگر Database نیز این Ruleها را enforce کند، یک Bug در Application به سادگی داده نامعتبر وارد Collection نمی‌کند.

Dependency Security

Driver MongoDB، Framework، ODM، JSON Parser و Validation Library باید به‌روز باشند.

Vulnerability ممکن است در:

Database Driver

ODM

Serialization Library

Authentication Package

Query Builder

یا Framework

باشد.

OWASP نیز Supply Chain و Dependencyهای آسیب‌پذیر را بخشی از تهدیدهای امنیتی NoSQL می‌داند.

در CI/CD می‌توان از Software Composition Analysis برای شناسایی نسخه‌های آسیب‌پذیر استفاده کرد.

Code Review برای NoSQL Injection

Code Review باید دنبال Data Flow باشد.

سؤال اصلی:

Input کاربر از کجا وارد می‌شود و در نهایت کجا به Query تبدیل می‌شود؟

Review روی Patternهای زیر اهمیت دارد:

استفاده مستقیم از req.body

استفاده مستقیم از req.query

Dynamic Filter

Raw JSON Query

String Concatenation

Dynamic JavaScript Expression

Aggregation Pipeline کنترل‌شده توسط Client

Update Object خام

Dynamic Sort Field

Dynamic Collection Name

عدم Type Validation

استفاده مستقیم از User ID ارسالی Client برای Ownership

Code Review معمولاً در یافتن NoSQL Injection بسیار مؤثر است؛ زیرا Source و Sink قابل شناسایی هستند.

SAST و DAST

Static Application Security Testing یا SAST می‌تواند برخی Data Flowهای مشکوک را در Source Code شناسایی کند.

Dynamic Application Security Testing یا DAST رفتار Runtime را بررسی می‌کند.

هیچ‌کدام به تنهایی کامل نیستند.

ترکیب مناسب:

Secure Code Review

SAST

DAST

Dependency Scan

Integration Test

و Penetration Test مجاز

دید بهتری فراهم می‌کند.

تست امنیت NoSQL Injection چگونه انجام شود؟

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

هدف تست دفاعی این است که مشخص شود:

آیا Endpoint ورودی Structured غیرمنتظره می‌پذیرد؟

آیا نوع داده Validate می‌شود؟

آیا Property اضافی Reject می‌شود؟

آیا Query Operatorهای Client-Controlled پذیرفته می‌شوند؟

آیا Errorهای Database افشا می‌شوند؟

آیا Query خام از Client گرفته می‌شود؟

آیا Login در برابر تغییر نوع داده مقاوم است؟

آیا Update Fieldها Allowlist هستند؟

آیا Tenant Scope سمت Server enforce می‌شود؟

آیا Database User کمترین دسترسی ممکن را دارد؟

نیازی نیست برای اثبات ضعف، داده واقعی استخراج یا تغییر داده حساس انجام شود.

OWASP نیز در راهنمای تست NoSQL تأکید می‌کند که برای اثبات Vulnerability لزوماً نیازی به Full Exploitation نیست. یک معماری امن برای استفاده از NoSQL

یک معماری امن برای استفاده از NoSQL

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

Client
   ↓
Reverse Proxy / WAF
   ↓
Authentication
   ↓
API Controller
   ↓
Schema Validation
   ↓
Authorization
   ↓
Business Logic
   ↓
Safe Query Builder
   ↓
Database Driver
   ↓
NoSQL Database

کنترل‌های مکمل:

Rate Limiting
Logging
Monitoring
RBAC
TLS
Network Segmentation
Secret Management
Database Schema Validation
Dependency Management

نکته مهم این است که Database Driver مستقیماً با Request خام صحبت نکند.

بین HTTP Request و Database باید چند Trust Boundary وجود داشته باشد.

نمونه جریان امن Login

یک Login امن از دید Query می‌تواند چنین Flowای داشته باشد:

Request
↓
JSON Parse
↓
Schema Validation
↓
username/password = String?
↓
Length Validation
↓
Lookup user by fixed username filter
↓
Password Hash Verification
↓
Account Status Check
↓
2FA در صورت فعال بودن
↓
Session / Token Creation

در این Flow هیچ مرحله‌ای اجازه نمی‌دهد Client ساختار Query را تعریف کند.

برای Search:

Search Request
↓
Validate allowed parameters
↓
Normalize values
↓
Apply page-size limit
↓
Map public filters to internal fields
↓
Build fixed query
↓
Apply tenant/user scope
↓
Execute with timeout
↓
Return limited fields

مهاجم حتی اگر بتواند Parameterها را کنترل کند، Database Query Language در اختیار او قرار نمی‌گیرد.

Projection و Data Minimization

فرض کنید Query فقط برای نمایش نام و Avatar کاربر است.

Database نباید تمام Document را برگرداند اگر نیازی به آن نیست.

Projection می‌تواند فقط Fieldهای لازم را دریافت کند.

مزیت آن:

کاهش داده در Memory

کاهش احتمال افشای Secret

کاهش Impact Bug

و پیاده‌سازی Data Minimization

است.

برای مثال Backend ممکن است اصلاً Password Hash یا Recovery Token را از Query معمول Profile دریافت نکند.

دفاع در برابر Data Exfiltration

اگر Query Injection رخ دهد، چند کنترل می‌توانند Impact را محدود کنند:

Least Privilege

Projection

Tenant Isolation

Field-Level Protection

Authorization

Rate Limiting

Monitoring

Network Segmentation

Encryption

و Audit Logging

هدف این است که یک ضعف Validation نتواند مستقیماً کل Database را در دسترس قرار دهد.

Backup و NoSQL Injection

Backup مستقیماً مانع Injection نیست، اما بخشی از Incident Resilience است.

اگر Vulnerability باعث تغییر یا حذف Data شود، Backup سالم می‌تواند برای Recovery ضروری باشد.

Backup باید:

رمزنگاری شود.

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

از Production Credential جدا باشد.

Restore آن تست شود.

Retention مشخص داشته باشد.

و در Public Storage رها نشود.

OWASP نیز Backup ناامن را یکی از Failure Modeهای محیط‌های NoSQL معرفی می‌کند.

اشتباهات رایج در جلوگیری از NoSQL Injection

اعتماد به اینکه «MongoDB SQL ندارد، پس Injection ندارد»

این یکی از رایج‌ترین برداشت‌های اشتباه است.

SQL Injection کلاسیک ممکن است وجود نداشته باشد، اما Query Injection، Operator Injection یا JavaScript Injection همچنان ممکن است ایجاد شود.

ارسال مستقیم req.body به Query

Request Body باید Data باشد، نه Database Query.

هر Field باید استخراج و Validate شود.

فقط فیلتر کردن Quote و Characterهای SQL

NoSQL لزوماً از SQL Syntax استفاده نمی‌کند.

فیلتر Character به تنهایی Defense قابل اعتماد نیست.

عدم بررسی Type

اگر Application انتظار String دارد، باید فقط String قبول کند.

Object، Array و Null نباید به شکل ضمنی پذیرفته شوند.

استفاده از Blocklist Operator

ممنوع کردن چند Operator شناخته‌شده کافی نیست.

Allowlist Field و Query Capability امن‌تر است.

استفاده از Database Admin Account در Application

Application معمولی نباید Credential سطح Administrator داشته باشد.

استفاده از Dynamic JavaScript بدون نیاز

اگر Server-Side JavaScript لازم نیست، بهتر است استفاده نشود و در صورت امکان غیرفعال شود.

نمایش Database Error

Stack Trace و Query Error نباید به Client فرستاده شوند.

اعتماد کامل به ODM

ODM می‌تواند کمک کند، اما Query ناامن همچنان می‌تواند از طریق Application ساخته شود.

نداشتن Rate Limit

Automated Probing و Query Abuse بدون Rate Limit آسان‌تر می‌شود.

نبود Monitoring

اگر Validation Failure و Query Error بررسی نشوند، حمله ممکن است مدت زیادی بدون Detection ادامه پیدا کند. چک‌لیست جلوگیری از NoSQL Injection

چک‌لیست جلوگیری از NoSQL Injection

  1. تمام Inputها را Untrusted در نظر بگیرید.
  2. برای Request Body از Schema Validation استفاده کنید.
  3. نوع هر Field را به‌صورت Strict بررسی کنید.
  4. Propertyهای ناشناخته را Reject کنید.
  5. Query Object خام را از Client نپذیرید.
  6. Operatorهای Database را در اختیار Client قرار ندهید مگر با Policy محدود.
  7. Query Structure را در Backend ثابت نگه دارید.
  8. Public Filter را به Query داخلی Map کنید.
  9. از String Concatenation برای Query Expression خودداری کنید.
  10. User Input را در JavaScript قابل اجرا قرار ندهید.
  11. Server-Side JavaScript غیرضروری را غیرفعال کنید.
  12. $where و Dynamic Evaluation را تا حد امکان حذف کنید.
  13. Fieldهای Update را Allowlist کنید.
  14. Mass Assignment را کنترل کنید.
  15. Login را با Type Validation محافظت کنید.
  16. Password را به شکل امن Hash کنید.
  17. Authorization را مستقل از Query اجرا کنید.
  18. Ownership Resource را سمت Server بررسی کنید.
  19. Tenant Scope را از Authentication Context استخراج کنید.
  20. Database Credential را براساس Least Privilege تنظیم کنید.
  21. برای هر Service Account جداگانه ایجاد کنید.
  22. RBAC را فعال کنید.
  23. Database Port را به اینترنت عمومی باز نکنید.
  24. از TLS برای Connectionهای حساس استفاده کنید.
  25. Credentialها را در Secret Manager نگه دارید.
  26. Schema Validation سمت Database را در صورت امکان فعال کنید.
  27. Query Size و Result Size را محدود کنید.
  28. Pagination اجباری اعمال کنید.
  29. Query Timeout مناسب داشته باشید.
  30. Rate Limiting را روی Endpointهای حساس فعال کنید.
  31. Errorهای داخلی Database را به User نشان ندهید.
  32. Logها را از Password و Token پاک نگه دارید.
  33. Validation Failureهای غیرعادی را Monitor کنید.
  34. Driver و ODM را به‌روز نگه دارید.
  35. Dependency Scanning را در CI/CD اجرا کنید.
  36. Code Review روی Query Construction انجام دهید.
  37. SAST و DAST را در فرایند امنیتی قرار دهید.
  38. Backup امن و قابل Restore داشته باشید.
  39. API Contract را مستند کنید.
  40. Database Query Language را از Public API جدا نگه دارید.

سؤالات متداول درباره NoSQL Injection

NoSQL Injection چیست؟

NoSQL Injection آسیب‌پذیری‌ای است که در آن Input غیرقابل‌اعتماد می‌تواند ساختار یا منطق Query پایگاه داده NoSQL را تغییر دهد. این مشکل معمولاً زمانی ایجاد می‌شود که Query Object، Operator یا Expression از ورودی Client ساخته شود و Validation کافی وجود نداشته باشد.

آیا MongoDB در برابر Injection آسیب‌پذیر است؟

MongoDB Queryهای عادی را با BSON Object می‌سازد و SQL Injection کلاسیک به همان شکل معمول مطرح نیست، اما Applicationهایی که Query Object خام، Operatorهای کنترل‌شده توسط Client یا JavaScript Dynamic استفاده می‌کنند ممکن است در برابر NoSQL Injection آسیب‌پذیر باشند.

آیا NoSQL Injection همان SQL Injection است؟

خیر. هر دو عضو خانواده Injection هستند، اما SQL Injection معمولاً ساختار SQL را هدف قرار می‌دهد، در حالی که NoSQL Injection می‌تواند JSON، BSON، Query Operator، Filter Object یا Expressionهای Database را تحت تأثیر قرار دهد.

آیا استفاده از MongoDB Driver جلوی NoSQL Injection را می‌گیرد؟

Driver استاندارد بسیاری از مشکلات Query String را کاهش می‌دهد، اما نمی‌تواند Application Logic ناامن را اصلاح کند. اگر برنامه Object دریافتی از Client را مستقیماً به Driver بدهد، همچنان ممکن است آسیب‌پذیری ایجاد شود.

مهم‌ترین راه جلوگیری از NoSQL Injection چیست؟

مهم‌ترین اصل این است که User فقط Data را کنترل کند و Query Structure در Backend ساخته شود. Schema Validation، Type Validation، Allowlist Fieldها و جلوگیری از Raw Query از کنترل‌های اساسی هستند.

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

خیر. Validation دقیق معمولاً مهم‌تر است. اگر Application String انتظار دارد، Object باید Reject شود؛ نه اینکه با چند فیلتر Character تلاش شود به ورودی امن تبدیل شود.

آیا WAF از NoSQL Injection جلوگیری می‌کند؟

WAF می‌تواند بعضی Patternها را شناسایی کند، اما به دلیل تفاوت Query Languageها و ساختار JSON نباید دفاع اصلی باشد. کنترل اصلی باید در Application و Database Permissionها اعمال شود.

آیا Mongoose جلوی NoSQL Injection را می‌گیرد؟

Mongoose امکاناتی مانند Schema و Type Casting فراهم می‌کند، اما استفاده نادرست از Filterهای خام، Dynamic Query یا Input کنترل‌نشده همچنان می‌تواند مشکل ایجاد کند. Framework جای Secure Coding را نمی‌گیرد.

آیا $where در MongoDB خطرناک است؟

$where امکان اجرای JavaScript در Query را فراهم می‌کند و نباید User Input وارد آن شود. MongoDB توصیه می‌کند تا حد امکان از Query Operatorهای استاندارد استفاده شود. Server-Side JavaScript نیز از MongoDB 8.0 Deprecated شده است.

آیا می‌توان JavaScript سمت Server را در MongoDB غیرفعال کرد؟

بله. اگر Application به Server-Side JavaScript نیاز ندارد، در Self-Managed MongoDB می‌توان با تنظیم security.javascriptEnabled: false یا گزینه --noscripting آن را غیرفعال کرد.

Schema Validation چه کمکی می‌کند؟

Schema Validation مشخص می‌کند Document باید چه Fieldها و Data Typeهایی داشته باشد. این کنترل می‌تواند از ذخیره Typeهای غیرمنتظره و تغییر ناخواسته Schema جلوگیری کند و یک لایه دفاعی تکمیلی ایجاد کند.

آیا NoSQL Injection می‌تواند باعث دور زدن Login شود؟

اگر Login Query به شکل ناامن ساخته شود و Query Logic تحت کنترل Input قرار گیرد، Authentication Logic ممکن است تحت تأثیر قرار گیرد. راهکار دفاعی، Strict Type Validation، Query ثابت و بررسی مستقل Password Hash است.

آیا NoSQL Injection می‌تواند باعث حذف اطلاعات شود؟

Impact به Query، Database Permission و معماری بستگی دارد. اگر Service Account دارای Permission زیاد باشد، پیامد آسیب‌پذیری می‌تواند شدیدتر شود. به همین دلیل Least Privilege اهمیت بسیار زیادی دارد.

Least Privilege چه ارتباطی با NoSQL Injection دارد؟

Least Privilege Vulnerability را حذف نمی‌کند، اما Blast Radius را کاهش می‌دهد. اگر Application فقط Permissionهای ضروری داشته باشد، حتی در صورت وجود Injection دسترسی Database محدودتر خواهد بود.

آیا Database باید پشت Firewall قرار بگیرد؟

در اغلب معماری‌های وب، Database باید در شبکه خصوصی باشد و فقط Application Serverهای مورد نیاز بتوانند به آن متصل شوند. Database Port و Management Interface نباید بدون دلیل در معرض Public Internet قرار گیرند.

چگونه NoSQL Injection را در سایت خود بررسی کنیم؟

بهترین نقطه شروع، Code Review Query Construction است. بررسی کنید آیا req.body، req.query یا Objectهای Client-Controlled مستقیماً وارد Database API می‌شوند. سپس Schema Validation، Type Validation، RBAC، Logging و Error Handling را Audit کنید.

جمع‌بندی

NoSQL Injection نشان می‌دهد تغییر فناوری Database باعث از بین رفتن اصول بنیادی امنیت نمی‌شود. ممکن است SQL دیگر وجود نداشته باشد، اما اگر Input غیرقابل‌اعتماد بتواند منطق یک Query را کنترل کند، خانواده Injection همچنان مطرح است.

در برنامه‌های MongoDB و سایر پایگاه‌های Document-Based، یکی از مهم‌ترین مرزهای امنیتی تفاوت میان Value و Query Object است.

Application باید خودش Query را بسازد.

Client فقط باید داده لازم را ارسال کند.

اگر Endpoint یک Email می‌خواهد، فقط Email String دریافت شود.

اگر Search Filter نیاز است، Public Filter محدود تعریف شود و Backend آن را به Query داخلی تبدیل کند.

Request Body کامل نباید بدون کنترل مستقیماً به Database Driver داده شود.

Type Validation در برنامه‌های JSONمحور اهمیت ویژه‌ای دارد. String، Object، Array، Number و Boolean ساختارهای متفاوتی هستند و Application نباید اجازه دهد Type غیرمنتظره وارد Query شود.

در کنار Validation، استفاده از Schema Validation، Field Allowlist، Query Builder امن و حذف Dynamic Evaluation می‌تواند سطح حمله را به‌طور قابل توجهی کاهش دهد.

قابلیت‌هایی مانند Server-Side JavaScript نیز باید فقط در صورت نیاز واقعی فعال باشند. در MongoDB 8.0 قابلیت‌های Server-Side JavaScript مانند $where، $function و $accumulator Deprecated شده‌اند و استفاده از Query Operatorهای استاندارد معمولاً انتخاب مناسب‌تری است.

اما جلوگیری از NoSQL Injection فقط به Input Validation محدود نمی‌شود.

Database Account برنامه باید براساس Least Privilege ساخته شود.

RBAC باید فعال باشد.

Database نباید بدون ضرورت در معرض اینترنت قرار گیرد.

Credentialها باید مدیریت و Rotate شوند.

TLS باید در محیط مناسب فعال باشد.

Errorهای داخلی نباید افشا شوند.

Log و Monitoring باید رفتار غیرعادی را آشکار کنند.

Dependencyها نیز باید به‌روز نگه داشته شوند.

در نهایت، معماری امن NoSQL بر یک اصل ساده استوار است:

ورودی کاربر باید داده باقی بماند و هیچ‌گاه بدون کنترل به بخشی از دستور یا منطق پایگاه داده تبدیل نشود.

اگر این مرز در تمام مراحل طراحی، توسعه و تست رعایت شود، بخش بزرگی از ریسک NoSQL Injection پیش از رسیدن برنامه به محیط Production حذف خواهد شد.

مطالب مرتبط