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

سه ستون اصلی امنیت API

امنیت API بر سه ستون بنا می‌شود: احراز هویت که مشخص می‌کند چه کسی درخواست می‌دهد، مجوزدهی که مشخص می‌کند آن کاربر چه کاری می‌تواند انجام دهد، و اعتبارسنجی که تضمین می‌کند داده ورودی با قواعد مطابقت دارد. نبود هر یک از این سه، API را در برابر تهدیدهای مشخصی آسیب‌پذیر می‌کند. در پروژه‌های واقعی، اکثر حوادث امنیتی به یکی از این سه ستون برمی‌گردند، نه به آسیب‌پذیری‌های پیچیده.

یک اشتباه رایج: تیم‌ها امنیت را یک بار در ابتدای پروژه پیاده می‌کنند و بعد فراموش می‌کنند. اما امنیت API یک وضعیت است، نه یک پروژه. با هر endpoint جدید، هر تغییر در مدل داده و هر آپدیت کتابخانه، باید امنیت را دوباره بررسی کنید. برای آشنایی با اصول کلی، راهنمای امنیت وردپرس برای مبتدیان چارچوب خوبی ارائه می‌دهد.

امنیت API، یک قفل نیست؛ یک فرآیند است. هر endpoint جدید، یک فرصت جدید برای بازبینی است.

احراز هویت: شناسایی کاربر

سه روش اصلی احراز هویت در API وجود دارد. اول، API Key که ساده‌ترین شکل است و برای سرویس‌های داخلی مناسب است. دوم، JWT یا JSON Web Token که امضای دیجیتال دارد و می‌تواند اطلاعات کاربر را در خود حمل کند. سوم، OAuth 2.0 که برای APIهای عمومی و اتصال به سرویس‌های خارجی استفاده می‌شود. برای آشنایی با JWT، JWT چیست و چه کاربردی در احراز هویت دارد و برای OAuth، OAuth چیست و چگونه کار می‌کند را ببینید.

در پروژه‌های واقعی، انتخاب بین این سه روش به ماهیت API بستگی دارد. API داخلی که فقط توسط سرویس‌های خودتان مصرف می‌شود، معمولاً با API Key کار می‌کند. API موبایل با کاربران نهایی، JWT انتخاب اول است. API عمومی که توسعه‌دهندگان خارجی از آن استفاده می‌کنند، OAuth 2.0 استاندارد صنعت است. راهنمای کامل این انتخاب در احراز هویت در API و احراز هویت در REST API آمده است.

در هر سه روش، چند اصل مشترک وجود دارد. اول، توکن‌ها همیشه با الگوریتم امن تولید شوند. در PHP، random_bytes و در پایتون، secrets ابزارهای امن هستند. دوم، توکن‌ها در دیتابیس hash شوند نه به صورت خام. سوم، توکن‌ها باید انقضا داشته باشند و قابل revoke باشند. چهارم، همه ارتباطات باید روی HTTPS باشد. بدون HTTPS، حتی قوی‌ترین توکن هم در معرض دزدی قرار دارد.

یک نکته که در پروژه‌ها زیاد دیدم: جداسازی کلیدهای محیط تست از محیط production. اگر همان API Key را در تست و تولید استفاده کنید، لو رفتن کلید تست به معنای لو رفتن production است. برای هر محیط، کلید جدا بسازید و امکان revoke سریع را در نظر بگیرید.

مجوزدهی: کنترل دسترسی

احراز هویت می‌گوید کی هستی؛ مجوزدهی می‌گوید چه کاری می‌توانی بکنی. این دو، دو مفهوم مستقل هستند و باید در لایه‌های جداگانه کد پیاده شوند. در REST، معمولاً مجوزدهی در سطح endpoint تعریف می‌شود. در GraphQL، در سطح فیلد. هر دو رویکرد نقاط قوت و ضعف خود را دارند و باید بر اساس معماری پروژه انتخاب شوند. برای مقایسه این دو، تفاوت REST و GraphQL راهنمای کاملی دارد.

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

یک اصل طلایی در مجوزدهی: کمترین دسترسی لازم (Principle of Least Privilege). هر کاربر باید فقط دسترسی‌هایی داشته باشد که برای انجام وظایفش لازم است. این اصل در سطح کلان ساده به نظر می‌رسد اما در پیاده‌سازی، نیاز به دقت زیادی دارد. برای الگوهای مدیریت دسترسی، تفاوت احراز هویت و مجوزدهی توضیح کاملی دارد.

اعتبارسنجی ورودی و پاک‌سازی

هر ورودی API، یک نقطه ورود بالقوه برای حمله است. دو مرحله باید روی هر ورودی انجام شود: اعتبارسنجی که فرمت و محدوده را بررسی می‌کند، و پاک‌سازی که کاراکترهای خطرناک را حذف یا بی‌اثر می‌کند. بی‌توجهی به هر کدام، به حمله‌های شناخته‌شده منجر می‌شود. برای درک تهدیدهای رایج، SQL Injection چیست و چگونه جلوگیری کنیم، حملات XSS و CSRF را ببینید.

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

برای پاک‌سازی در سمت دیتابیس، استفاده از prepared statement اجباری است. این تنها روش مطمئن برای جلوگیری از SQL Injection است. پاک‌سازی دستی کاراکترها ناکافی است و می‌تواند دور زده شود. در PHP، PDO و MySQLi این قابلیت را دارند. در پایتون، اکثر ORMها و کتابخانه‌های دیتابیس، prepared statement را به صورت خودکار استفاده می‌کنند. برای درک عمیق‌تر، بهینه‌سازی پیشرفته دیتابیس وردپرس نکات کاربردی دارد.

محدودسازی نرخ درخواست

محدودسازی نرخ درخواست (Rate Limiting) دو هدف دارد: جلوگیری از حمله DoS (Denial of Service) و جلوگیری از سوءاستفاده از API توسط یک کاربر خاص. بدون rate limiting، یک مهاجم می‌تواند با ارسال درخواست‌های زیاد، سرور را از کار بیندازد. برای درک این نوع حمله، حمله DDoS چیست و چگونه دفع می‌شود و حمله Brute Force چیست را ببینید.

پیاده‌سازی rate limiting در چند سطح امکان‌پذیر است: در سطح IP، در سطح کاربر (بر اساس توکن) و در سطح endpoint. در پروژه‌ها، معمولاً ترکیبی از این سه سطح توصیه می‌شود. الگوی من این است: محدودیت پایه برای همه IPها، محدودیت دقیق‌تر برای هر توکن، و محدودیت سخت‌گیرانه برای endpointهای حساس مثل لاگین و ورود اطلاعات.

روش‌های ذخیره‌سازی شمارنده rate limit در پروژه‌ها: در محیط‌های کوچک، فایل یا دیتابیس کافی است. در محیط‌های بزرگ با ترافیک بالا، استفاده از Redis یا Memcached توصیه می‌شود چون عملیات read/write بسیار سریع دارند. یک نکته ظریف: نرخ rate limit باید با احتیاط تنظیم شود. اگر خیلی سختگیرانه باشد، کاربران واقعی را هم مسدود می‌کند. اگر خیلی آزاد باشد، جلوی حمله را نمی‌گیرد. بهترین روش، تنظیم اولیه سخاوتمندانه و بعد از تحلیل ترافیک، سختگیرانه‌تر کردن است.

امنیت انتقال داده

هر ارتباط API باید روی HTTPS باشد. این یک قاعده ساده است اما در پروژه‌ها هنوز هم نقض می‌شود. HTTPS از دو جهت مهم است: رمزنگاری داده در مسیر و احراز هویت سرور. بدون HTTPS، یک مهاجم در وسط مسیر (Man-in-the-Middle) می‌تواند داده‌ها را ببیند، تغییر دهد یا دزدیده شود. برای درک این حمله، حمله MITM چیست و چه خطراتی دارد را ببینید.

علاوه بر HTTPS، چند تنظیم امنیتی مرتبط با TLS باید رعایت شود: استفاده از نسخه‌های جدید TLS (1.2 به بالا)، غیرفعال کردن cipher suites ضعیف، فعال کردن HSTS (HTTP Strict Transport Security) و پین کردن گواهی برای ارتباطات حساس. اگر روی هاست اشتراکی هستید، برخی از این تنظیمات ممکن است توسط ارائه‌دهنده محدود شوند؛ در این حالت، استفاده از CDN یا پراکسی می‌تواند این محدودیت‌ها را پوشش دهد. برای آشنایی با نقش CDN در امنیت، فایروال ابری و سنتی تحلیل کاملی دارد.

هدرهای امنیتی و CORS

هدرهای امنیتی HTTP، لایه‌ای هستند که مرورگر را در برابر حملات مختلف محافظت می‌کنند. چند هدر مهم که در پروژه‌های API توصیه می‌شود: Strict-Transport-Security که HTTPS را اجباری می‌کند، X-Content-Type-Options که از حدس نوع فایل توسط مرورگر جلوگیری می‌کند، X-Frame-Options که از clickjacking محافظت می‌کند و Content-Security-Policy که منابع قابل بارگذاری را محدود می‌کند. راهنمای کامل در هدرهای امنیتی HTTP آمده است.

CORS (Cross-Origin Resource Sharing) یکی از پرچالش‌ترین بخش‌های امنیت API است. اگر CORS را با * تنظیم کنید، هر سایتی می‌تواند به API شما درخواست بفرستد. اگر آن را خیلی محدود کنید، اپلیکیشن‌های خودتان کار نمی‌کنند. الگوی حرفه‌ای، تعریف دقیق دامنه‌های مجاز در هدر CORS و جدا کردن آن از احراز هویت است. برای امنیت API، CORS کافی نیست و احراز هویت هم لازم است؛ این دو مکمل هستند نه جایگزین.

یک نکته که در پروژه‌ها زیاد دیدم: فراموش کردن preflight request. مرورگرها قبل از درخواست‌های پیچیده، یک درخواست OPTIONS می‌فرستند که باید پاسخ مناسب بگیرد. اگر این پاسخ درست تنظیم نشود، همه درخواست‌های پیچیده شکست می‌خورند.

لاگ‌گیری و پایش

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

چند نکته عملی در لاگ‌گیری: اول، لاگ‌ها در فضایی خارج از سرور اصلی ذخیره شوند تا در صورت نفوذ، مهاجم نتواند آن‌ها را پاک کند. دوم، لاگ‌ها باید rotate شوند تا فضای دیسک پر نشود. سوم، باید ابزار تحلیل روی لاگ‌ها اعمال شود تا الگوهای مشکوک را نشان دهد. چهارم، برخی درخواست‌ها مثل rate limit breach و authentication failure باید هشدار فوری بسازند.

برای پایش فعالانه، چند شاخص کلیدی را در نظر بگیرید: تعداد درخواست‌های 401 و 403 که ممکن است نشانه تلاش برای نفوذ باشد، تعداد درخواست‌های 5xx که نشانه مشکلات سرور است، تغییرات ناگهانی در ترافیک که ممکن است نشانه حمله باشد، و درخواست‌های از IPهای خارج از محدوده جغرافیایی معمول. با پایش این شاخص‌ها، می‌توانید قبل از فاجعه، واکنش نشان دهید.

لاگ بدون تحلیل، فقط فایل سنگین است. لاگ با تحلیل و هشدار، سیستم هشدار سریع امنیت API شماست.

پرسش‌های پرتکرار درباره امنیت API

آیا HTTPS برای امنیت API کافی است؟ خیر. HTTPS فقط از انتقال داده محافظت می‌کند. احراز هویت، مجوزدهی، اعتبارسنجی و rate limiting هم لازم هستند.

آیا API Key یا JWT بهتر است؟ بستگی به سناریو دارد. API Key برای سرویس‌های داخلی ساده‌تر و مناسب‌تر است. JWT برای اپلیکیشن‌های کاربر-محور مزایای بیشتری دارد چون انقضا و اطلاعات کاربر را در خود حمل می‌کند. برای درک عمیق‌تر، احراز هویت در API را ببینید.

چگونه API را در برابر حملات DoS محافظت کنم؟ ترکیب rate limiting، استفاده از CDN با DDoS protection، و پایش ترافیک. برای درک این نوع حمله، حمله DDoS را ببینید.

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

آیا باید API را با 2FA محافظت کنم؟ 2FA برای کاربران نهایی که با مرورگر وارد می‌شوند توصیه می‌شود اما برای API که توسط برنامه‌ها مصرف می‌شود، معمولاً توکن امن کافی است. برای 2FA، تأثیر 2FA بر امنیت را ببینید.

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

درس‌هایی از پروژه‌های واقعی

سه چیز بعد از سال‌ها کار با امنیت API در ذهنم جا افتاده. اول، هیچ‌وقت فرض نکنید که API شما از خارج قابل دسترسی نیست؛ همیشه فرض کنید که هست و بر اساس آن طراحی کنید. دوم، امنیت را در معماری بگنجانید نه به عنوان لایه‌ای که بعداً اضافه می‌شود. سوم، پایش فعالانه بدون هشدار، بی‌فایده است. برای پیاده‌سازی ساختاری امنیت، امن‌سازی پروژه‌های توسعه وردپرس، امنیت فروشگاه ووکامرس و قفل‌کردن فایل wp-config را ببینید. برای ساختار امنیتی حرفه‌ای، هدرهای امنیتی HTTP، فایروال نرم‌افزاری در سرور و فایروال ابری و سنتی راهنمای کاملی هستند.

اگر تجربه‌ای از یک چالش امنیتی در API خود داشته‌اید - چه کشف شده و چه در ممیزی - در دیدگاه بنویسید. برای من جالب است بدانم کدام بخش از امنیت API، بیشترین چالش را در پروژه‌های واقعی برای شما ایجاد کرده است. 🔒