امنیت 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 را میتوان به چند دسته اصلی تقسیم کرد:
- Broken Access Control و مشکلات Authorization
- BOLA یا دسترسی غیرمجاز به Objectها
- دسترسی غیرمجاز به Fieldهای حساس
- Introspection و افشای Schema
- Queryهای عمیق و پیچیده و حملات DoS
- GraphQL Batching و Alias Abuse
- Injection
- CSRF
- Rate Limiting ناقص
- افشای اطلاعات از Errorها
- Mass Assignment و تغییر Propertyهای غیرمجاز
- مشکلات Resolver و N+1
- ضعف Authentication و Token Management
- ریسک Subscription و WebSocket
- Security Misconfiguration
- مشکلات Federation و Microservice Architecture
در ادامه هر مورد را دقیقتر بررسی میکنیم. 
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 است و اجازه میدهد درباره 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 چیست؟
یکی از شناختهشدهترین مشکلات امنیت 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 اجازه میدهند چند 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
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 امن
یک معماری دفاعی مناسب را میتوان به چند لایه تقسیم کرد.
لایه اول: 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 Query | DoS و افزایش هزینه Backend | Cost Analysis |
| Unlimited Pagination | مصرف RAM، DB و Bandwidth | Maximum Page Size |
| Batching Abuse | دورزدن Rate Limit ساده | Operation Limit |
| Alias Abuse | چند اجرای Function در یک Query | Alias/Root Field Limit |
| Injection | دسترسی یا اجرای ناخواسته در Backend | Validation و Parameterization |
| SSRF | دسترسی Server-side به مقصد ناامن | Destination Allowlist |
| CSRF | اجرای Operation از مرورگر قربانی | CSRF Protection و Content-Type Validation |
| Verbose Errors | افشای معماری داخلی | Safe Error Handling |
| Open Introspection | افزایش اطلاعات سطح حمله | Restrict Introspection |
| N+1 | مصرف شدید Database | DataLoader و 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 استفاده کرد بدون اینکه انعطافپذیری آن به یک سطح حمله کنترلنشده تبدیل شود.