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

امنیت GraphQL چیست؟ مهم‌ترین آسیب‌پذیری‌های GraphQL و روش‌های جلوگیری

امنیت GraphQL به مجموعه روش‌هایی گفته می‌شود که از APIهای GraphQL در برابر دسترسی غیرمجاز، افشای اطلاعات، حملات DoS، Injection، CSRF و سوءاستفاده از Queryهای پیچیده محافظت می‌کنند. مهم‌ترین خطرها شامل ضعف Authorization در سطح Object و Field، Queryهای بدون محدودیت، Batching Abuse و تنظیمات ناامن Production هستند. استفاده از Depth و Complexity Limit، Pagination، Rate Limiting، Input Validation، کنترل دسترسی دقیق و Monitoring از مهم‌ترین راهکارهای افزایش امنیت GraphQL محسوب می‌شوند.

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

امنیت GraphQL مجموعه‌ای از کنترل‌ها، معماری‌ها و روش‌های دفاعی است که از APIهای مبتنی بر GraphQL در برابر دسترسی غیرمجاز، افشای اطلاعات، Injection، حملات DoS، سوءاستفاده از Queryهای پیچیده، نقص Authorization، CSRF و سایر تهدیدهای API محافظت می‌کند. مهم‌ترین اصل این است که انعطاف‌پذیری GraphQL نباید به معنی آزادی نامحدود کلاینت برای دسترسی به داده و مصرف منابع سرور باشد.

GraphQL در بسیاری از برنامه‌های مدرن به دلیل انعطاف بالا، کاهش Over-fetching و امکان درخواست دقیق داده محبوب شده است. با این حال، همین انعطاف اگر بدون کنترل امنیتی مناسب پیاده‌سازی شود، می‌تواند سطح حمله قابل‌توجهی ایجاد کند. برخلاف یک API ساده REST که معمولاً Endpointهای مشخص و رفتار نسبتاً ثابتی دارد، در GraphQL یک Endpoint ممکن است صدها نوع Query، Mutation، فیلد، ارتباط و Resolver را در اختیار کلاینت قرار دهد.

به همین دلیل امنیت GraphQL تنها با قرار دادن یک WAF یا فعال‌کردن Authentication تأمین نمی‌شود. توسعه‌دهنده باید از مرحله طراحی Schema تا Resolverها، Authorization، محدودیت Query، مانیتورینگ، مدیریت خطاها و محافظت از منابع زیرساخت، امنیت را در کل چرخه درخواست در نظر بگیرد.

در این مقاله از رخنه‌کاو بررسی می‌کنیم GraphQL چگونه کار می‌کند، چرا مدل امنیتی آن با APIهای سنتی تفاوت دارد، مهم‌ترین آسیب‌پذیری‌های GraphQL چیست و چگونه می‌توان یک GraphQL API را به شکل اصولی و دفاعی ایمن کرد.

GraphQL چیست و چرا امنیت آن اهمیت دارد؟

GraphQL یک زبان Query و Runtime برای APIها است که به کلاینت اجازه می‌دهد مشخص کند دقیقاً چه داده‌ای نیاز دارد. در معماری‌های سنتی REST ممکن است برای دریافت اطلاعات کاربر، سفارش‌ها و جزئیات هر سفارش به چند Endpoint مختلف نیاز داشته باشیم، اما در GraphQL اغلب می‌توان این داده‌ها را از یک Endpoint و در قالب یک Query دریافت کرد.

برای مثال، یک Schema ممکن است مفهومی شبیه ساختار زیر داشته باشد:

type User {
  id: ID!
  name: String!
  orders: [Order!]!
}

type Order {
  id: ID!
  status: String!
  total: Float!
}

type Query {
  user(id: ID!): User
}

این ساختار به کلاینت اجازه می‌دهد فقط فیلدهای موردنیاز خود را درخواست کند. قابلیت Client-specified Queries یکی از مزیت‌های اصلی GraphQL است و مشخصات رسمی GraphQL نیز تأکید می‌کند که پاسخ می‌تواند دقیقاً شامل داده‌هایی باشد که کلاینت درخواست کرده است. GraphQL همچنین دارای سیستم Introspection است که امکان پرس‌وجو درباره ساختار Schema را فراهم می‌کند.

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

به همین دلیل سؤال اصلی در امنیت GraphQL فقط این نیست که:

«آیا کاربر وارد حساب شده است؟»

بلکه باید پرسید:

«این کاربر اجازه اجرای این Operation را دارد؟»

«آیا به این Object دسترسی دارد؟»

«آیا مجاز به مشاهده این Field است؟»

«Query ارسال‌شده چه مقدار CPU، حافظه، Database Query یا درخواست Backend مصرف می‌کند؟»

«آیا می‌توان چند عملیات حساس را داخل یک درخواست ترکیب کرد؟»

«اگر درخواست هزاران Resolver را فعال کند چه اتفاقی می‌افتد؟»

این موارد امنیت GraphQL را به موضوعی فراتر از Authentication ساده تبدیل می‌کنند.

اجزای GraphQL که باید از دید امنیتی بشناسیم

برای درک آسیب‌پذیری‌های GraphQL ابتدا باید چند مفهوم اصلی را بشناسیم.

Schema

Schema قرارداد اصلی API است و مشخص می‌کند چه Typeها، Queryها، Mutationها و فیلدهایی وجود دارند.

Schema را می‌توان نقشه سطح حمله GraphQL در نظر گرفت. هر قابلیت اضافه‌شده به Schema باید از نظر مجوز دسترسی و پیامد امنیتی بررسی شود.

Query

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

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

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

Mutation

Mutation برای عملیات تغییر‌دهنده وضعیت مانند ایجاد، ویرایش یا حذف داده استفاده می‌شود.

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

Resolver

Resolver کدی است که هنگام درخواست یک Field اجرا شده و اطلاعات را از پایگاه داده، Microservice، فایل، API خارجی یا منبع دیگری دریافت می‌کند.

حتی اگر Schema کاملاً صحیح به نظر برسد، یک Resolver ناامن می‌تواند باعث SQL Injection، SSRF، Broken Access Control یا افشای اطلاعات شود.

Variables

Variables برای ارسال مقادیر ورودی به Operation استفاده می‌شوند.

استفاده از سیستم Type در GraphQL بخشی از اعتبارسنجی ساختاری ورودی را فراهم می‌کند، اما Type Validation جایگزین اعتبارسنجی امنیتی نیست.

برای مثال، اینکه یک ورودی از نوع String است به معنی امن‌بودن آن برای استفاده در SQL Query، Shell Command، URL یا Template نیست.

Subscription

Subscription برای دریافت داده‌های Real-time استفاده می‌شود و معمولاً ارتباط بلندمدتی مانند WebSocket ایجاد می‌کند.

امنیت Subscription علاوه بر Authorization به موضوعاتی مانند محدودیت Connection، احراز هویت اتصال، تمدید Token، تعداد Subscriptionها و مصرف منابع نیز وابسته است.

آیا GraphQL ذاتاً ناامن است؟

خیر. GraphQL ذاتاً یک فناوری ناامن محسوب نمی‌شود.

بخش بزرگی از آسیب‌پذیری‌های GraphQL ناشی از طراحی و پیاده‌سازی اشتباه API است، نه خود زبان GraphQL. PortSwigger نیز اشاره می‌کند که آسیب‌پذیری‌های GraphQL معمولاً از نقص‌های Implementation و Design ایجاد می‌شوند.

مشکل اصلی این است که برخی قابلیت‌های مفید GraphQL در صورت استفاده بدون محدودیت می‌توانند سطح حمله را گسترش دهند.

برای مثال:

Introspection برای ابزارهای توسعه بسیار مفید است.

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

Batching می‌تواند تعداد درخواست‌های شبکه را کاهش دهد.

Nested Queries امکان دریافت روابط پیچیده را فراهم می‌کنند.

Subscriptions قابلیت Real-time ایجاد می‌کنند.

اما همین قابلیت‌ها اگر بدون کنترل امنیتی ارائه شوند، می‌توانند برای شناسایی Schema، دورزدن Rate Limit ساده، افزایش مصرف منابع یا گسترش سطح حمله مورد سوءاستفاده قرار گیرند.

بنابراین امنیت GraphQL باید براساس اصل Defense in Depth یا «دفاع چندلایه» طراحی شود. مهم‌ترین آسیب‌پذیری‌های GraphQL

مهم‌ترین آسیب‌پذیری‌های GraphQL

تهدیدهای GraphQL را می‌توان به چند دسته اصلی تقسیم کرد:

  1. Broken Access Control و مشکلات Authorization
  2. BOLA یا دسترسی غیرمجاز به Objectها
  3. دسترسی غیرمجاز به Fieldهای حساس
  4. Introspection و افشای Schema
  5. Queryهای عمیق و پیچیده و حملات DoS
  6. GraphQL Batching و Alias Abuse
  7. Injection
  8. CSRF
  9. Rate Limiting ناقص
  10. افشای اطلاعات از Errorها
  11. Mass Assignment و تغییر Propertyهای غیرمجاز
  12. مشکلات Resolver و N+1
  13. ضعف Authentication و Token Management
  14. ریسک Subscription و WebSocket
  15. Security Misconfiguration
  16. مشکلات Federation و Microservice Architecture

در ادامه هر مورد را دقیق‌تر بررسی می‌کنیم. Broken Access Control در GraphQL

Broken Access Control در GraphQL

یکی از مهم‌ترین اشتباهات امنیت GraphQL این است که توسعه‌دهنده Authentication را با Authorization اشتباه بگیرد.

Authentication مشخص می‌کند:

«کاربر چه کسی است؟»

Authorization مشخص می‌کند:

«این کاربر اجازه انجام چه کاری را دارد؟»

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

فرض کنیم Schema شامل Query زیر باشد:

user(id: ID!): User

اگر Resolver فقط ID را از کلاینت دریافت کند و رکورد مربوطه را برگرداند، بدون اینکه مالکیت یا مجوز دسترسی بررسی شود، API ممکن است دچار Broken Object Level Authorization شود.

OWASP در API Security Top 10 این مشکل را با عنوان BOLA معرفی می‌کند و تأکید می‌کند هر تابع API که شناسه یک Object را دریافت می‌کند باید بررسی کند آیا کاربر فعلی مجوز انجام عملیات موردنظر روی همان Object را دارد یا خیر.

روش صحیح Authorization

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

یک منطق دفاعی ساده می‌تواند چنین مفهومی داشته باشد:

object = loadObject(requestedId)

if object does not exist:
    return safe error

if currentUser cannot read object:
    deny access

return object

نکته کلیدی این است که وجود ID یا حتی غیرقابل حدس بودن آن نباید مجوز دسترسی تلقی شود.

UUID نیز Authorization نیست.

اگر مهاجم به هر روش UUID یک Object را به دست آورد، همچنان سرور باید بررسی کند آیا مجاز به مشاهده آن Object است یا خیر.

BOLA و IDOR در GraphQL

Broken Object Level Authorization یکی از خطرناک‌ترین مشکلات API است.

در GraphQL این مشکل ممکن است در Query یا Mutation رخ دهد.

برای مثال:

order(id: ID!): Order

یا:

updateOrder(id: ID!, input: OrderInput!): Order

در هر دو حالت، سرور باید بررسی کند کاربر فعلی چه رابطه‌ای با Order موردنظر دارد.

یک اشتباه رایج این است که توسعه‌دهنده Query مربوط به لیست سفارش‌ها را امن کند اما Resolver مربوط به order(id) را فراموش کند.

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

OWASP توصیه می‌کند کنترل Authorization روی تمام مسیرهایی که Object را براساس شناسه ورودی کاربر بازیابی می‌کنند اعمال شود.

دفاع در برابر BOLA

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

  • هویت کاربر
  • مالکیت Object
  • Tenant یا سازمان مربوطه
  • Role کاربر
  • Scope یا Permission
  • نوع Operation
  • وضعیت Object
  • قوانین Business Logic

در معماری Multi-tenant این موضوع اهمیت بیشتری دارد.

اگر برنامه چند شرکت یا Workspace را میزبانی می‌کند، Query پایگاه داده نیز باید Tenant Scope مناسب داشته باشد.

برای مثال صرفاً بررسی user_id کافی نیست اگر همان User در چند Workspace نقش‌های متفاوتی دارد.

Broken Field-Level Authorization

یکی از ویژگی‌های GraphQL این است که کلاینت انتخاب می‌کند چه Fieldهایی را دریافت کند.

همین ویژگی باعث می‌شود Authorization در سطح Field اهمیت زیادی داشته باشد.

فرض کنیم Type کاربر چنین باشد:

type User {
  id: ID!
  name: String!
  email: String!
  phone: String
  internalNote: String
}

ممکن است کاربر اجازه مشاهده name را داشته باشد اما نباید internalNote یا حتی email شخص دیگری را ببیند.

اگر سرور فقط در سطح Query بررسی کند که کاربر اجازه مشاهده User را دارد، احتمال دارد Fieldهای حساس ناخواسته در دسترس قرار گیرند.

OWASP API Security Top 10 این کلاس مشکل را در Broken Object Property Level Authorization پوشش می‌دهد و به‌طور مشخص اشاره می‌کند که در GraphQL ممکن است کاربر با Query سفارشی Propertyهای بیشتری را درخواست کند.

راهکار

Permission باید در سطح مناسب تعریف شود.

برای برخی APIها Object-level Authorization کافی است.

برای داده‌های حساس‌تر ممکن است Field-level Authorization لازم باشد.

بهتر است Schema نیز طوری طراحی شود که اطلاعات عمومی و خصوصی بی‌دلیل داخل یک Type واحد قرار نگیرند.

اصل Least Privilege یا «حداقل سطح دسترسی» باید در طراحی Schema نیز اعمال شود.

Broken Function Level Authorization در Mutationها

نوع دیگری از ضعف Authorization زمانی رخ می‌دهد که کاربر به Function یا Mutationی دسترسی پیدا کند که مخصوص Role دیگری است.

فرض کنید Schema دارای Mutationهایی برای کاربران عادی و مدیران باشد.

اگر فقط رابط Front-end گزینه مدیریت را مخفی کند اما Backend هیچ Authorization واقعی نداشته باشد، کاربر می‌تواند مستقیماً Operation را به GraphQL Endpoint ارسال کند.

OWASP این مسئله را Broken Function Level Authorization یا BFLA می‌نامد و توصیه می‌کند کنترل دسترسی به‌صورت پیش‌فرض Deny باشد و برای هر Function مجوز صریح تعریف شود.

بنابراین پنهان‌کردن دکمه Admin، حذف لینک از UI یا استفاده از نام غیرقابل حدس برای Mutation هیچ‌کدام کنترل امنیتی محسوب نمی‌شوند.

Authorization باید Server-side باشد. Introspection در GraphQL چیست و چه خطری دارد؟

Introspection در GraphQL چیست و چه خطری دارد؟

Introspection یکی از قابلیت‌های اصلی GraphQL است و اجازه می‌دهد درباره Schema سؤال شود.

ابزارهای توسعه می‌توانند از این قابلیت برای شناخت Typeها، Fieldها، Arguments، Mutationها و ساختار API استفاده کنند.

مشخصات رسمی GraphQL سیستم Introspection را بخشی از معماری GraphQL تعریف می‌کند.

در محیط Development این ویژگی بسیار مفید است.

اما در Production، خصوصاً برای APIهای خصوصی یا داخلی، Introspection عمومی می‌تواند حجم زیادی از اطلاعات درباره سطح حمله را آشکار کند.

اطلاعاتی مانند:

  • نام Queryها
  • Mutationها
  • Typeها
  • Arguments
  • ارتباط Objectها
  • Fieldهای Deprecated
  • توضیحات Schema

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

OWASP توصیه می‌کند در محیط‌های Production یا APIهای عمومی، Introspection و ابزارهایی مانند GraphiQL براساس نیاز واقعی محدود یا غیرفعال شوند.

آیا خاموش‌کردن Introspection امنیت GraphQL را تأمین می‌کند؟

خیر.

این نکته بسیار مهم است.

خاموش‌کردن Introspection فقط مقدار اطلاعات مستقیم در دسترس مهاجم را کاهش می‌دهد.

اگر Authorization وجود نداشته باشد، API همچنان ناامن است.

همچنین بعضی Implementationها در پاسخ به نام Field اشتباه پیشنهادهایی مشابه «Did you mean ...?» ارائه می‌کنند. بنابراین حتی با Introspection غیرفعال، امکان حدس‌زدن بخشی از Schema ممکن است وجود داشته باشد.

Security by Obscurity نباید جایگزین کنترل امنیتی واقعی شود. Query Depth Attack چیست؟

Query Depth Attack چیست؟

یکی از شناخته‌شده‌ترین مشکلات امنیت GraphQL امکان ساخت Queryهای بسیار عمیق است.

GraphQL اجازه می‌دهد Objectهای مرتبط به‌صورت Nested درخواست شوند.

برای مثال در یک سیستم اجتماعی:

User → Posts → Comments → Author → Posts → Comments

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

هر سطح ممکن است Resolverهای جدید، Database Queryهای بیشتر و مصرف حافظه بالاتر ایجاد کند.

در نتیجه یک درخواست HTTP نسبتاً کوچک ممکن است کار بسیار بزرگی در Backend ایجاد کند.

OWASP برای کاهش ریسک DoS در GraphQL استفاده از Depth Limiting را توصیه می‌کند.

Depth Limit چیست؟

سرور قبل از اجرای Query عمق آن را محاسبه می‌کند.

اگر عمق بیشتر از مقدار تعیین‌شده باشد، درخواست رد می‌شود.

برای مثال ممکن است Application براساس ساختار واقعی خود عمق ۸ یا ۱۰ را بپذیرد و Queryهای عمیق‌تر را رد کند.

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

Depth Limit باید با توجه به Schema، Use Caseهای واقعی و هزینه Resolverها تعیین شود.

Query Complexity و Cost Analysis

محدودیت Depth به‌تنهایی کافی نیست.

دو Query با عمق برابر ممکن است هزینه کاملاً متفاوتی داشته باشند.

برای مثال یکی تنها اطلاعات یک Profile را دریافت می‌کند اما دیگری در همان عمق هزاران Record را از چند Service جمع‌آوری می‌کند.

اینجاست که Query Complexity یا Cost Analysis اهمیت پیدا می‌کند.

در این روش به Fieldها یا Operationها Cost اختصاص داده می‌شود.

برای نمونه:

Field ساده: هزینه ۱

جست‌وجوی دیتابیس: هزینه بیشتر

لیست بزرگ: هزینه براساس تعداد آیتم

Resolver خارجی: هزینه بالاتر

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

اگر مقدار آن از Budget مجاز عبور کند، درخواست اجرا نمی‌شود.

پیش‌نویس GraphQL over HTTP نیز از محدودیت اندازه Document، زمان اجرا، Pagination، Depth و Complexity به‌عنوان نمونه‌هایی از کنترل‌های امنیتی قابل اعمال نام می‌برد.

چرا Cost Analysis از Rate Limiting دقیق‌تر است؟

فرض کنیم دو کاربر هر کدام ۱۰ درخواست در دقیقه ارسال کنند.

کاربر اول Queryهای ساده اجرا می‌کند.

کاربر دوم Queryهایی اجرا می‌کند که هر کدام صدها Resolver و Database Operation فعال می‌کنند.

Rate Limit مبتنی بر تعداد HTTP Request هر دو را یکسان می‌بیند.

اما مصرف واقعی منابع یکسان نیست.

برای GraphQL بهتر است Rate Limiting با Cost Budget ترکیب شود.

Pagination و جلوگیری از دریافت حجم نامحدود داده

لیست‌هایی مانند Users، Products، Orders یا Comments نباید بدون محدودیت در اختیار Client قرار گیرند.

اگر پارامتری مانند first یا limit وجود دارد، سرور باید Maximum مشخصی برای آن تعیین کند.

برای مثال اگر برنامه به‌طور منطقی در هر صفحه حداکثر ۱۰۰ رکورد نیاز دارد، نباید کلاینت بتواند میلیون‌ها رکورد درخواست کند.

OWASP در بحث Unrestricted Resource Consumption به محدودیت تعداد Recordهای هر صفحه، اندازه Payload، Execution Timeout و تعداد عملیات داخل درخواست اشاره می‌کند.

Pagination علاوه بر Performance یک کنترل امنیتی نیز محسوب می‌شود. GraphQL Batching Attack چیست؟

GraphQL Batching Attack چیست؟

برخی پیاده‌سازی‌های GraphQL اجازه می‌دهند چند Operation در یک HTTP Request ارسال شود.

این قابلیت Batching نام دارد.

Batching برای Performance مفید است، اما اگر Rate Limiting فقط تعداد Requestهای HTTP را بشمارد، یک Client ممکن است تعداد زیادی عملیات را در یک Request قرار دهد.

برای مثال Rate Limiter فکر می‌کند یک Request دریافت شده است، در حالی که Backend ده‌ها یا صدها Operation را پردازش می‌کند.

OWASP این مسئله را به‌عنوان یکی از ریسک‌های خاص GraphQL مطرح کرده و توصیه می‌کند تعداد Operationهای قابل Batch محدود شود و برای عملیات حساس محدودیت در سطح Object یا Operation اعمال گردد.

سوءاستفاده از Aliasها

Alias یکی از امکانات مشروع GraphQL است که به Client اجازه می‌دهد چند بار یک Field را با نام‌های متفاوت در یک Operation درخواست کند.

اما اگر کنترل‌های امنیتی فقط بر تعداد Requestهای HTTP تمرکز کنند، Aliasها می‌توانند رفتار Rate Limiting را پیچیده کنند.

برای مثال یک Operation ممکن است چند بار یک Function حساس را فراخوانی کند.

دفاع مناسب این نیست که Alias را به‌طور کامل ممنوع کنیم.

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

  • تعداد Alias
  • تعداد Root Field
  • تعداد Operation
  • Complexity کل Query
  • تعداد اجرای Functionهای حساس
  • Budget کاربر در بازه زمانی

PortSwigger نیز محدودیت تعداد Aliasها و Root Fieldها را بخشی از Operation Limits مناسب برای دفاع در برابر Abuse معرفی می‌کند.

حملات DoS در GraphQL

Denial of Service یا DoS در GraphQL فقط به معنی ارسال میلیون‌ها Request نیست.

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

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

CPU

RAM

Database Connections

Network Connections

External APIs

Storage

Thread Pool

Worker Pool

Serverless Invocation

حتی Serviceهایی که بابت هر Call هزینه دریافت می‌کنند.

OWASP API Security Top 10 این موضوع را تحت Unrestricted Resource Consumption قرار می‌دهد و تأکید می‌کند درخواست‌های API می‌توانند علاوه بر منابع فنی، هزینه عملیاتی سرویس‌های جانبی را نیز افزایش دهند.

دفاع چندلایه در برابر GraphQL DoS

یک API مقاوم معمولاً چند کنترل را هم‌زمان به‌کار می‌گیرد:

Depth Limit

Complexity Limit

Pagination

Request Size Limit

Timeout

Rate Limit

Operation Limit

Concurrency Limit

Circuit Breaker

Cache

Resource Limit در Container

Monitoring

Persisted Operations در سناریوهای مناسب

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

N+1 Problem و تأثیر آن بر امنیت

N+1 در اصل یک مشکل Performance است اما می‌تواند اثر امنیتی نیز داشته باشد.

فرض کنیم برای دریافت ۱۰۰ کاربر یک Query اجرا شود.

سپس Resolver مربوط به سفارش‌های هر User یک Query جداگانه به Database بفرستد.

به جای چند Query محدود، Backend ممکن است صدها Query اجرا کند.

در شرایط عادی این مسئله باعث کندی می‌شود.

اما زمانی که Query Shape را Client تعیین می‌کند، یک مهاجم یا حتی Client اشتباه می‌تواند این رفتار را تشدید کند.

ابزارهایی مانند DataLoader یا Server-side Batching/Caching می‌توانند تعداد فراخوانی‌های تکراری Backend را کاهش دهند. OWASP نیز به Server-side Batching و Caching برای کاهش مصرف منابع GraphQL اشاره می‌کند.

با این حال DataLoader جای Complexity Limit را نمی‌گیرد.

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

Injection در GraphQL

برخی توسعه‌دهندگان تصور می‌کنند چون GraphQL دارای Type System است، مشکلاتی مانند SQL Injection دیگر رخ نمی‌دهند.

این تصور اشتباه است.

GraphQL ورودی را دریافت می‌کند اما در نهایت Resolver ممکن است آن را به:

SQL Database

NoSQL Database

Search Engine

Operating System

Template Engine

HTTP Client

LDAP

یا سرویس دیگری منتقل کند.

اگر داده کاربر بدون Validation و روش امن در Backend استفاده شود، Injection همچنان ممکن است رخ دهد.

OWASP در GraphQL Cheat Sheet به SQL Injection، NoSQL Injection، OS Command Injection، SSRF و مشکلات مشابه به‌عنوان تهدیدهای قابل توجه اشاره می‌کند.

روش جلوگیری از Injection

GraphQL Type Validation فقط اولین لایه است.

در Backend باید از موارد زیر استفاده شود:

Parameterized Queries برای Database

ORM امن با Binding صحیح

Allowlist برای مقادیر محدود

Validation براساس Context

عدم ساخت Command با Concatenation

عدم ساخت URL حساس مستقیماً از ورودی کاربر

Escaping متناسب با Output Context

حداقل‌سازی Permission حساب Database

تفکیک Resolverهای حساس

برای مثال اگر ورودی قرار است Country Code باشد، بهتر است فقط مجموعه محدودی از Country Codeهای مجاز پذیرفته شود، نه اینکه هر String دلخواه وارد Backend شود.

SSRF در Resolverها

Resolver ممکن است برای دریافت داده با APIهای داخلی یا خارجی ارتباط برقرار کند.

اگر Client بتواند URL یا Host مورد درخواست Resolver را کنترل کند، احتمال Server-Side Request Forgery یا SSRF وجود دارد.

در SSRF، سرور به نمایندگی از Client یک Request به مقصدی ارسال می‌کند که نباید قابل دسترسی باشد.

دفاع مناسب شامل Allowlist مقصدها، کنترل Scheme، محدودیت Redirect، تفکیک Network و اعتبارسنجی دقیق ورودی است.

OWASP نیز صراحتاً توصیه می‌کند تا زمانی که نیاز تجاری قطعی وجود ندارد، Backend نباید Host مقصد درخواست را مستقیماً از ورودی کاربر دریافت کند.

Mass Assignment در Mutationها

Mutationها معمولاً Input Object دریافت می‌کنند.

یک الگوی خطرناک این است که تمام Input دریافتی بدون انتخاب صریح Fieldها مستقیماً روی Model اعمال شود.

برای مثال Object کاربر شاید شامل فیلدهایی مانند:

name

email

role

credit

isAdmin

باشد.

اگر API فقط قصد داشته باشد name قابل تغییر باشد اما Backend هر Property ورودی را به Model منتقل کند، خطر تغییر فیلدهای حساس ایجاد می‌شود.

دفاع اصولی استفاده از Allowlist است.

یعنی Resolver دقیقاً مشخص کند چه Fieldهایی در هر Mutation قابل تغییر هستند.

مثلاً:

allowed:
- name
- avatar
- language

server-only:
- role
- credit
- isAdmin

Authorization باید علاوه بر Object در سطح Property نیز بررسی شود.

CSRF در GraphQL

Cross-Site Request Forgery یا CSRF زمانی رخ می‌دهد که مرورگر کاربر احراز هویت‌شده وادار شود یک درخواست ناخواسته به برنامه هدف ارسال کند.

GraphQL نیز می‌تواند در شرایط خاص در برابر CSRF آسیب‌پذیر باشد.

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

درخواست GET برای Operationهای حساس بپذیرد،

POSTهای ساده مانند application/x-www-form-urlencoded را بپذیرد،

Content-Type را اعتبارسنجی نکند،

و Authentication مبتنی بر Cookie بدون CSRF Protection داشته باشد.

پیش‌نویس GraphQL over HTTP هشدار می‌دهد که Media Typeهایی مانند multipart/form-data یا application/x-www-form-urlencoded ممکن است در مرورگر به‌صورت Simple Request ارسال شوند و بدون طراحی صحیح زمینه CSRF ایجاد کنند.

PortSwigger نیز توصیه می‌کند GraphQL API در سناریوهای مناسب درخواست‌ها را به POST با JSON محدود کند، Content-Type را اعتبارسنجی کند و مکانیزم CSRF Token مناسب داشته باشد.

CORS با CSRF یکسان نیست

یکی از اشتباهات رایج این است که توسعه‌دهندگان CORS را جایگزین CSRF Protection تصور می‌کنند.

CORS مشخص می‌کند چه Originهایی تحت قوانین مرورگر اجازه خواندن یا ارسال برخی Cross-origin Requestها را دارند.

اما CSRF مسئله دیگری است و به ارسال Request معتبر از مرورگر کاربر با Credential مربوط می‌شود.

بنابراین تنظیم CORS باید دقیق باشد، اما نباید تنها کنترل CSRF باشد.

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

SameSite Cookie

CSRF Token

Origin Validation

Content-Type Validation

محدودیت HTTP Method

Cookieهای Secure و HttpOnly

Authentication در GraphQL

GraphQL یک سیستم Authentication خاص تحمیل نمی‌کند.

برنامه می‌تواند از Session Cookie، JWT، OAuth 2.0، OpenID Connect یا روش دیگری استفاده کند.

اما چند اصل اهمیت ویژه دارند:

Token باید اعتبارسنجی شود.

Expiration باید بررسی شود.

Signature باید معتبر باشد.

Audience و Issuer در معماری مربوطه بررسی شوند.

Refresh Token باید محافظت شود.

Session Revocation باید در طراحی دیده شود.

Authentication نباید فقط در Front-end اعمال شود.

همچنین Authentication باید قبل از Resolverهای حساس انجام شود تا Resolverها بتوانند تصمیم Authorization صحیح بگیرند.

Rate Limiting مناسب GraphQL

Rate Limiting برای GraphQL باید بیشتر از شمارش IP باشد.

یک سیستم مناسب ممکن است هم‌زمان چند Dimension را بررسی کند:

IP

User ID

API Key

Tenant

Operation

Mutation حساس

Object

Query Cost

تعداد Resultها

Concurrency

برای مثال Login یا درخواست OTP باید محدودیت مستقل داشته باشد.

Query عمومی محصول ممکن است Limit متفاوتی داشته باشد.

گزارش‌گیری سنگین نیز باید Budget مخصوص خودش را داشته باشد.

این رویکرد از Rate Limit ساده HTTP بسیار دقیق‌تر است.

Persisted Queries و Safelisting

در برخی معماری‌ها Clientهای Production مجموعه نسبتاً مشخصی از Operationها دارند.

در چنین شرایطی می‌توان از Persisted Operations یا Safelist استفاده کرد.

ایده این است که سرور به جای پذیرش هر Query دلخواه، فقط Operationهایی را اجرا کند که قبلاً تأیید شده‌اند.

Apollo نیز Safelisting را یکی از روش‌های کاهش سطح حمله Queryهای ناخواسته معرفی می‌کند.

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

اگر API عمومی برای توسعه‌دهندگان خارجی ارائه می‌شود، ممکن است اجرای Queryهای Arbitrary بخشی از نیاز محصول باشد.

اما برای یک Mobile App یا Web App کنترل‌شده، Safelist می‌تواند سطح حمله را به شکل قابل‌توجهی محدود کند.

باز هم باید توجه داشت که Safelist جای Authorization را نمی‌گیرد.

اگر Operation تأییدشده به Object غیرمجاز دسترسی بدهد، همچنان آسیب‌پذیر خواهد بود.

Error Handling و افشای اطلاعات

پیام‌های خطای GraphQL باید برای Client مفید باشند، اما نباید اطلاعات داخلی حساس را آشکار کنند.

نمایش موارد زیر در Production خطرناک است:

Stack Trace کامل

Database Error

SQL Query

File Path

Internal Hostname

Environment Variable

Library Versionهای غیرضروری

Token

Secret

جزئیات زیرساخت

خطای عمومی Client می‌تواند اطلاعات لازم را ارائه دهد، در حالی که جزئیات کامل در Log داخلی ذخیره شود.

OWASP توصیه می‌کند Debug Mode و Errorهای بیش از حد در Production غیرفعال شوند.

امنیت GraphiQL و Playground

GraphiQL و ابزارهای مشابه برای Development بسیار مفید هستند.

اما فعال‌بودن بدون محدودیت آن‌ها در Production می‌تواند فرآیند شناسایی API را برای فرد غیرمجاز ساده کند.

برای API داخلی یا خصوصی بهتر است این ابزارها:

در Production خاموش باشند،

یا پشت Authentication قرار گیرند،

یا فقط از Network مدیریتی قابل دسترسی باشند.

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

امنیت GraphQL Subscription و WebSocket

Subscriptionها معمولاً Connection طولانی ایجاد می‌کنند و در نتیجه کنترل‌های متفاوتی نسبت به Requestهای عادی لازم دارند.

موارد مهم شامل:

Authentication هنگام Connection

Authorization برای هر Subscription

محدودیت تعداد Subscription هم‌زمان

Connection Timeout

Idle Timeout

حداکثر Message Size

Rate Limit پیام‌ها

مدیریت Token Expiration

بستن Connectionهای غیرمعتبر

یک خطای مهم این است که کاربر هنگام ایجاد WebSocket Authentication شود اما بعد از تغییر Role، خروج از حساب یا منقضی‌شدن Session همچنان Connection قبلی دسترسی داشته باشد.

طراحی سیستم باید Lifecycle احراز هویت Connection را نیز در نظر بگیرد.

امنیت GraphQL Federation

در معماری Federation چند Subgraph یا Service پشت یک Graph واحد قرار می‌گیرند.

این معماری مزایای زیادی دارد اما Trust Boundaryهای بیشتری نیز ایجاد می‌کند.

برای مثال باید مشخص باشد:

چه سیستمی می‌تواند مستقیماً به Subgraph متصل شود؟

آیا Subgraph فقط از Router قابل دسترسی است؟

Identity چگونه بین Serviceها منتقل می‌شود؟

Authorization در Gateway انجام می‌شود یا Service؟

اگر Router دور زده شود چه اتفاقی می‌افتد؟

آیا Service داخلی به Header ارسالی Client اعتماد می‌کند؟

Apollo در توصیه‌های امنیتی خود بر Defense in Depth، محدودکردن دسترسی مستقیم به Subgraphها و اجرای Authentication و Authorization در لایه‌های مناسب تأکید می‌کند.

اصل مهم در Federation

«Internal» بودن یک Service به‌تنهایی کنترل امنیتی نیست.

شبکه داخلی نیز باید Trust Boundary داشته باشد.

Service نباید Header جعلی مانند User Role را بدون اعتبارسنجی از هر منبعی قبول کند.

WAF چه نقشی در امنیت GraphQL دارد؟

Web Application Firewall یا WAF می‌تواند بخشی از دفاع GraphQL باشد، اما نباید تنها خط دفاعی محسوب شود.

WAF می‌تواند در مواردی مانند:

Rate Limiting

IP Reputation

Request Size Limit

Bot Management

Pattern Detection

Traffic Filtering

کمک کند.

اما Authorization در سطح Object، Field یا Business Logic را معمولاً نمی‌توان صرفاً به WAF سپرد.

WAF نمی‌داند آیا User شماره ۵ مجاز است Order شماره ۲۷ را مشاهده کند یا خیر.

این تصمیم باید در Application یا Authorization Layer گرفته شود.

بنابراین WAF مکمل امنیت GraphQL است، نه جایگزین آن.

اشتباهات رایج در امنیت GraphQL

تصور اینکه Authentication کافی است

ورود موفق کاربر تنها هویت را مشخص می‌کند.

برای هر عملیات حساس Authorization نیز لازم است.

اعتماد به UUID

UUID حدس‌زدن Object را سخت‌تر می‌کند اما جای Access Control را نمی‌گیرد.

خاموش‌کردن Introspection و تصور حل‌شدن مشکل

Introspection Reduction فقط Discovery را سخت‌تر می‌کند.

اگر Resolver ناامن باشد، ضعف اصلی همچنان وجود دارد.

Rate Limit فقط براساس HTTP Request

یک GraphQL Request ممکن است چند Operation یا تعداد زیادی Resolver اجرا کند.

نداشتن Pagination

لیست‌های نامحدود می‌توانند باعث مصرف زیاد Database، RAM و پهنای باند شوند.

محدودکردن Depth بدون Complexity

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

اعتماد مستقیم به Input GraphQL

Type System ورودی را از لحاظ Schema بررسی می‌کند، نه از لحاظ امنیت Backend.

اعمال Authorization فقط روی Root Query

Nested Resolverها و Fieldهای حساس نیز ممکن است به کنترل دسترسی نیاز داشته باشند.

نمایش Stack Trace در Production

این اطلاعات می‌تواند معماری داخلی برنامه را آشکار کند.

کنترل دسترسی فقط در Front-end

هر تصمیم امنیتی مهم باید در سمت Server اعمال شود. معماری پیشنهادی برای GraphQL امن

معماری پیشنهادی برای GraphQL امن

یک معماری دفاعی مناسب را می‌توان به چند لایه تقسیم کرد.

لایه اول: Edge

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

TLS

WAF

Request Size Limit

Basic Rate Limiting

Bot Protection

IP Rules

لایه دوم: GraphQL Gateway

در Gateway می‌توان موارد زیر را بررسی کرد:

Authentication

Operation Validation

Depth Limit

Complexity Limit

Operation Limit

Persisted Query

Timeout

Global Rate Limit

لایه سوم: Authorization

در این لایه Permission واقعی بررسی می‌شود:

Role

Scope

Tenant

Object Ownership

Field Permission

Business Rule

لایه چهارم: Resolver

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

لایه پنجم: Data Source

Database و Serviceهای داخلی نیز باید حداقل Permission لازم را داشته باشند.

اگر Application دچار ضعف شد، Backend نباید دسترسی نامحدود به کل زیرساخت داشته باشد.

لایه ششم: Monitoring

رویدادهای مهم باید ثبت و تحلیل شوند.

امنیت بدون Visibility ناقص است.

مانیتورینگ امنیت GraphQL

GraphQL Logging نباید فقط شامل Status Code HTTP باشد.

گاهی Endpoint همیشه HTTP 200 برمی‌گرداند اما پاسخ GraphQL حاوی Error است.

اطلاعات مفید برای Monitoring می‌تواند شامل موارد زیر باشد:

Operation Name

User ID یا شناسه امن

Tenant

Query Complexity

Query Depth

Execution Time

تعداد Resolverها

تعداد Database Queryها

تعداد Resultها

Error Type

Rate Limit Event

Blocked Operation

Mutationهای حساس

Introspection Attempt در محیط ممنوع

توجه شود که Log نباید Secret، Password، Token کامل یا داده حساس غیرضروری ذخیره کند.

چگونه GraphQL را به‌صورت امن تست کنیم؟

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

هدف تست دفاعی این است که مشخص شود آیا Policyهای تعریف‌شده واقعاً اعمال می‌شوند یا خیر.

۱. تهیه Inventory

تمام Endpointها، Schemaها، Queryها، Mutationها و Subscriptionهای شناخته‌شده مشخص شوند.

۲. بررسی Authentication

مشخص شود Operationهای خصوصی بدون Session یا Token معتبر قابل اجرا نیستند.

۳. تست Authorization در سطح Object

برای Objectهای مختلف بررسی شود آیا User A می‌تواند به Object متعلق به User B دسترسی داشته باشد.

۴. تست Field Permission

بررسی شود کاربران با Roleهای مختلف دقیقاً چه Fieldهایی را مشاهده می‌کنند.

۵. تست Mutation

تمام Mutationهای حساس از نظر Role، Ownership و Business Logic ارزیابی شوند.

۶. بررسی Resource Limits

Depth، Complexity، Pagination، Request Size و Timeout بررسی شوند.

۷. بررسی Introspection

مشخص شود Policy Production درباره Introspection مطابق طراحی اجرا شده است یا خیر.

۸. بررسی Errorها

Response نباید Stack Trace و اطلاعات داخلی غیرضروری فاش کند.

۹. بررسی CSRF و CORS

روش‌های HTTP، Content-Type، Cookie Policy، Origin Policy و CSRF Protection بررسی شوند.

۱۰. بررسی Logging

فعالیت‌های حساس باید قابل Audit باشند.

نمونه مدل Threat Modeling برای GraphQL

Threat Modeling کمک می‌کند قبل از Production مشکلات معماری شناسایی شوند.

برای هر Operation می‌توان پرسید:

چه کسی اجازه اجرای آن را دارد؟

ورودی از کجا می‌آید؟

چه داده‌ای برمی‌گرداند؟

به چه Database یا Serviceهایی متصل می‌شود؟

بدترین حالت مصرف منابع چقدر است؟

آیا نتیجه شامل PII است؟

آیا عملیات State را تغییر می‌دهد؟

آیا قابل Batch شدن است؟

آیا Audit لازم دارد؟

اگر Backend Service در دسترس نباشد چه اتفاقی می‌افتد؟

اگر User Role هنگام Session تغییر کند چه می‌شود؟

این مدل باعث می‌شود امنیت قبل از مرحله Penetration Testing وارد طراحی شود.

مقایسه امنیت GraphQL و REST

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

تفاوت اصلی در شکل سطح حمله است.

در REST معمولاً Endpointها رفتار مشخص‌تری دارند.

در GraphQL تعداد زیادی قابلیت ممکن است از یک Endpoint قابل دسترسی باشد و Client کنترل بیشتری روی شکل Query داشته باشد.

GraphQL معمولاً به کنترل‌های بیشتری برای Query Complexity، Depth و Field-level Authorization نیاز دارد.

از طرف دیگر Schema قوی GraphQL می‌تواند Validation و Governance را نیز بهتر کند.

بنابراین پاسخ درست این نیست که «REST امن‌تر است» یا «GraphQL امن‌تر است».

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

جدول مهم‌ترین آسیب‌پذیری‌های GraphQL

آسیب‌پذیریپیامد احتمالیکنترل اصلی
BOLA / IDORدسترسی به اطلاعات سایر کاربرانObject-level Authorization
Broken Field Authorizationافشای Fieldهای حساسField-level Permission
BFLAاجرای Function مدیریتیRole/Scope Validation
Deep Queryافزایش شدید مصرف منابعDepth Limit
Complex QueryDoS و افزایش هزینه BackendCost Analysis
Unlimited Paginationمصرف RAM، DB و BandwidthMaximum Page Size
Batching Abuseدورزدن Rate Limit سادهOperation Limit
Alias Abuseچند اجرای Function در یک QueryAlias/Root Field Limit
Injectionدسترسی یا اجرای ناخواسته در BackendValidation و Parameterization
SSRFدسترسی Server-side به مقصد ناامنDestination Allowlist
CSRFاجرای Operation از مرورگر قربانیCSRF Protection و Content-Type Validation
Verbose Errorsافشای معماری داخلیSafe Error Handling
Open Introspectionافزایش اطلاعات سطح حملهRestrict Introspection
N+1مصرف شدید DatabaseDataLoader و Query Optimization
Subscription Abuseمصرف Connection و دسترسی طولانیConnection/Subscription Limit

چک‌لیست امنیت GraphQL قبل از Production

پیش از انتشار GraphQL API بهتر است حداقل موارد زیر بررسی شوند:

  • Authentication تمام Operationهای خصوصی فعال است.
  • Object-level Authorization روی تمام Resolverهای مرتبط اعمال شده است.
  • Fieldهای حساس Permission مستقل دارند.
  • Mutationهای مدیریتی Role Check دارند.
  • Schema اطلاعات غیرضروری منتشر نمی‌کند.
  • Introspection براساس نیاز واقعی Production تنظیم شده است.
  • GraphiQL و Playground عمومی غیرضروری غیرفعال هستند.
  • Depth Limit تعریف شده است.
  • Complexity Limit تعریف شده است.
  • Maximum Page Size وجود دارد.
  • Request Size محدود شده است.
  • Execution Timeout وجود دارد.
  • تعداد Alias و Root Field کنترل می‌شود.
  • Batching محدود شده است.
  • Rate Limiting فقط براساس IP نیست.
  • Operationهای حساس Rate Limit اختصاصی دارند.
  • ورودی Resolverها Validation می‌شوند.
  • Queryهای Database پارامتری هستند.
  • URLهای Backend از Allowlist استفاده می‌کنند.
  • Errorها Stack Trace عمومی ندارند.
  • CORS دقیق تنظیم شده است.
  • CSRF برای Authentication مبتنی بر Cookie بررسی شده است.
  • Token و Session به‌درستی منقضی می‌شوند.
  • Subscriptionها Authorization مستقل دارند.
  • Database از Principle of Least Privilege پیروی می‌کند.
  • Log امنیتی وجود دارد.
  • Secretها داخل Log ذخیره نمی‌شوند.
  • Alert برای رفتارهای غیرعادی تعریف شده است.
  • تست Authorization در CI/CD یا تست امنیت دوره‌ای وجود دارد.

اولویت‌بندی اقدامات امنیتی

اگر یک تیم تازه قصد ایمن‌سازی GraphQL API موجود را دارد، بهتر است ابتدا کنترل‌هایی را پیاده‌سازی کند که بیشترین Risk Reduction را ایجاد می‌کنند.

اولویت نخست باید Authorization باشد.

وجود BOLA یا BFLA می‌تواند مستقیماً باعث افشای داده یا تغییر غیرمجاز اطلاعات شود.

در مرحله بعد باید Resource Consumption کنترل شود.

Depth Limit، Complexity Limit، Pagination، Timeout و Rate Limit تأثیر زیادی بر مقاومت API در برابر Abuse دارند.

سپس باید Input Validation، CSRF، Error Handling و تنظیمات Production بررسی شوند.

در نهایت Monitoring، Alerting و Governance باعث می‌شوند مشکلات جدید سریع‌تر شناسایی شوند.

آیا Disable کردن Introspection همیشه توصیه می‌شود؟

پاسخ مطلق وجود ندارد.

برای API داخلی یا API که فقط Clientهای مشخص دارد، محدودکردن Introspection در Production معمولاً منطقی است.

برای یک Public GraphQL API که توسعه‌دهندگان خارجی باید Schema را کشف کنند، Introspection ممکن است بخشی از محصول باشد.

در این شرایط بهتر است Schema عمداً Public طراحی شود و هیچ Field حساسی صرفاً به امید ناشناخته‌ماندن محافظت نشود.

PortSwigger نیز میان API عمومی و خصوصی تفاوت قائل می‌شود و تأکید می‌کند اگر Introspection لازم است، Schema باید از نظر افشای Fieldهای ناخواسته بررسی شود.

بنابراین سؤال صحیح این نیست که:

«Introspection روشن باشد یا خاموش؟»

بلکه باید پرسید:

«چه کسی به Introspection نیاز دارد و چه اطلاعاتی مجاز است از Schema بداند؟»

نقش اصل Least Privilege در امنیت GraphQL

Least Privilege باید در تمام لایه‌ها اعمال شود.

کاربر فقط Permission لازم را داشته باشد.

Resolver فقط به Service موردنیاز دسترسی داشته باشد.

Service Account فقط Queryهای لازم Database را اجرا کند.

Subgraph فقط از مسیر مجاز قابل دسترسی باشد.

Admin Operation برای User عادی در دسترس نباشد.

Schema فقط قابلیت موردنیاز Product را ارائه دهد.

هر قابلیت اضافی به معنی سطح حمله اضافی است.

یکی از بهترین روش‌های افزایش امنیت GraphQL حذف Capabilityهایی است که Application واقعاً به آن‌ها نیاز ندارد.

Secure by Design در GraphQL

امنیت GraphQL نباید در انتهای پروژه به‌عنوان یک افزونه اضافه شود.

بهتر است هنگام طراحی Schema تصمیم‌های امنیتی نیز مشخص شوند.

برای هر Type:

مالک آن چه کسی است؟

چه کسی می‌تواند آن را بخواند؟

چه کسی می‌تواند تغییر دهد؟

کدام Field حساس است؟

آیا داده Tenant-specific است؟

برای هر Query:

حداکثر Cost چیست؟

Pagination چگونه اعمال می‌شود؟

آیا Public است؟

برای هر Mutation:

چه Roleهایی اجازه اجرا دارند؟

آیا نیازمند Re-authentication است؟

آیا Audit می‌شود؟

آیا عملیات Idempotent است؟

این رویکرد احتمال ایجاد نقص‌هایی را کاهش می‌دهد که بعداً با Patchهای متعدد اصلاح شوند.

سؤالات متداول درباره امنیت GraphQL

امنیت GraphQL چیست؟

امنیت GraphQL مجموعه اقداماتی برای محافظت از GraphQL API در برابر دسترسی غیرمجاز، افشای داده، Queryهای پرهزینه، Injection، CSRF، DoS و سایر تهدیدها است. مهم‌ترین بخش‌های آن Authorization صحیح، محدودیت Query، Validation، Rate Limiting و Monitoring هستند.

آیا GraphQL از REST ناامن‌تر است؟

خیر. هیچ‌کدام ذاتاً امن‌تر نیستند. GraphQL به دلیل Queryهای انعطاف‌پذیر، Nested Fields و Schema غنی نیازمند کنترل‌هایی مانند Depth Limit، Complexity Analysis و Field-level Authorization است، اما در صورت طراحی صحیح می‌تواند به شکل امن استفاده شود.

مهم‌ترین آسیب‌پذیری GraphQL چیست؟

Broken Access Control یکی از مهم‌ترین ریسک‌ها است. اگر سرور مالکیت Object، Role، Scope و Permission را بررسی نکند، کاربر ممکن است به اطلاعات یا Functionهایی دسترسی پیدا کند که مجاز به استفاده از آن‌ها نیست.

آیا فعال بودن Introspection یک آسیب‌پذیری است؟

همیشه نه. Introspection یک قابلیت استاندارد GraphQL است. اما در APIهای خصوصی Production می‌تواند اطلاعات زیادی درباره Schema آشکار کند. بهتر است براساس مدل تهدید و نیاز محصول محدود شود و هرگز به‌عنوان جایگزین Authorization در نظر گرفته نشود.

آیا غیرفعال‌کردن Introspection برای امنیت کافی است؟

خیر. مهاجم ممکن است از روش‌های دیگری برای شناخت API استفاده کند و اگر Authorization یا Input Validation ضعیف باشد، مشکل اصلی همچنان باقی می‌ماند.

Query Depth چیست؟

Query Depth تعداد سطوح Nested در یک Query است. Queryهای بسیار عمیق ممکن است تعداد زیادی Resolver و Database Query اجرا کنند و منابع زیادی مصرف کنند. به همین دلیل تعیین حداکثر Depth یکی از کنترل‌های رایج امنیت GraphQL است.

Query Complexity چیست؟

Query Complexity روشی برای تخمین هزینه اجرای یک Query است. Fieldهای مختلف می‌توانند Cost متفاوتی داشته باشند و Server Queryهایی را که از Budget مشخص عبور می‌کنند قبل از اجرا رد کند.

آیا Rate Limiting معمولی برای GraphQL کافی است؟

در بسیاری از سیستم‌ها خیر. یک Request GraphQL می‌تواند شامل چند Operation یا Query پیچیده باشد. بهتر است Rate Limit با Complexity، Operation Count، User، Tenant و محدودیت Functionهای حساس ترکیب شود.

GraphQL چگونه در برابر DoS محافظت می‌شود؟

ترکیبی از Depth Limit، Complexity Analysis، Pagination، Request Size Limit، Timeout، Rate Limiting، Batching Limit، Concurrency Control، Cache و محدودیت منابع زیرساخت می‌تواند ریسک DoS را کاهش دهد.

آیا GraphQL در برابر SQL Injection امن است؟

Type System GraphQL به‌تنهایی مانع SQL Injection نمی‌شود. Resolver باید از Parameterized Query، ORM امن، Input Validation و Principle of Least Privilege استفاده کند.

آیا WAF می‌تواند GraphQL را امن کند؟

WAF لایه مهمی برای Rate Limiting، Bot Protection و کنترل Traffic است، اما قادر نیست به‌تنهایی Business Authorization را اعمال کند. امنیت GraphQL به کنترل‌های داخل Application نیز نیاز دارد.

آیا GraphQL می‌تواند دچار CSRF شود؟

بله. به‌خصوص اگر Authentication مبتنی بر Cookie باشد و Endpoint روش‌ها یا Content-Typeهایی را بپذیرد که Browser قادر به ارسال Cross-site آن‌ها است. طراحی صحیح CSRF Protection، Cookie Policy و Content-Type Validation اهمیت دارد.

بهترین روش محافظت از Mutation چیست؟

Authentication، Role/Scope Validation، Object-level Authorization، Field-level Authorization، Input Allowlist، Rate Limit، Audit Logging و Validation قوانین Business Logic باید هم‌زمان در نظر گرفته شوند.

جمع‌بندی

امنیت GraphQL بیش از آنکه به یک تنظیم یا ابزار خاص وابسته باشد، به معماری صحیح API بستگی دارد. GraphQL قدرت زیادی به Client می‌دهد و همین قابلیت اگر بدون کنترل ارائه شود می‌تواند به دسترسی غیرمجاز، افشای اطلاعات یا مصرف شدید منابع Backend منجر شود.

مهم‌ترین اصل، جداسازی Authentication از Authorization است. اینکه یک کاربر وارد سیستم شده به این معنی نیست که به تمام Objectها، Fieldها یا Mutationها دسترسی دارد. Object-level Authorization، Field-level Permission و کنترل Functionهای حساس باید در سمت Server و براساس Policy مشخص اعمال شوند.

در کنار کنترل دسترسی، باید هزینه Query نیز مدیریت شود. Depth Limit، Query Complexity، Pagination، Timeout، Rate Limiting و محدودیت Batching از مهم‌ترین لایه‌های دفاعی در برابر DoS و Abuse هستند.

Introspection، GraphiQL، Aliases، Batching و Subscription همگی قابلیت‌های مشروع GraphQL هستند، اما باید متناسب با نیاز واقعی Product در Production محدود شوند.

ورودی GraphQL نیز نباید به‌عنوان داده امن تلقی شود. Resolverها همچنان می‌توانند در معرض SQL Injection، NoSQL Injection، SSRF و دیگر مشکلات رایج Backend قرار گیرند. Parameterized Query، Validation، Allowlist و Least Privilege همچنان ضروری هستند.

در نهایت یک GraphQL API امن باید با رویکرد Defense in Depth طراحی شود: از Edge و WAF گرفته تا Gateway، Authorization، Resolver، Database و Monitoring. هیچ کنترل واحدی نمی‌تواند تمام تهدیدها را متوقف کند.

اگر Schema از ابتدا براساس Secure by Design ساخته شود و Authorization، Query Cost، Resource Limits و Logging بخشی از معماری اصلی باشند، می‌توان از مزایای GraphQL استفاده کرد بدون اینکه انعطاف‌پذیری آن به یک سطح حمله کنترل‌نشده تبدیل شود.

مطالب مرتبط