دسته بندی:

CGI چیست؟

CGI مخفف Common Gateway Interface است؛ یک استاندارد که مشخص می‌کند وب‌سرور چگونه باید یک برنامه‌ی بیرونی را اجرا کند، اطلاعات درخواست HTTP را به آن تحویل بدهد و خروجی آن برنامه را به‌عنوان پاسخ برای مرورگر بفرستد. به زبان ساده، CGI پلی است میان وب‌سرور و برنامه‌ای که خودش وب‌سرور نیست.

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

CGI دقیقاً چه کار می‌کند؟

وقتی درخواستی به مسیری می‌رسد که به یک اسکریپت CGI نگاشت شده است، وب‌سرور یک پروسه‌ی مستقل برای آن اسکریپت می‌سازد و مراحل زیر طی می‌شود:

  • مشخصات درخواست در قالب متغیرهای محیطی به پروسه داده می‌شود؛ متغیرهایی مانند REQUEST_METHOD، QUERY_STRING، CONTENT_TYPE، CONTENT_LENGTH، SCRIPT_NAME، PATH_INFO و REMOTE_ADDR. هدرهای HTTP هم با پیشوند HTTP_ به همین شکل منتقل می‌شوند.
  • بدنه‌ی درخواست (مثلاً داده‌های یک فرم POST) روی ورودی استاندارد برنامه ریخته می‌شود.
  • هر چه برنامه روی خروجی استاندارد بنویسد، پاسخ محسوب می‌شود: نخست چند سطر هدر مانند Content-Type، سپس یک خط کاملاً خالی و بعد از آن بدنه‌ی پاسخ.
  • با پایان کار برنامه، پروسه بسته می‌شود و برای درخواست بعدی همه‌چیز از صفر تکرار می‌شود.

نسخه‌ی رسمی این استاندارد با نام CGI/1.1 در سند RFC 3875 مستند شده است. نکته‌ی مهم این است که CGI به هیچ زبان برنامه‌نویسی خاصی وابسته نیست؛ هر زبانی که بتواند متغیر محیطی بخواند و روی خروجی استاندارد بنویسد، برای نوشتن CGI مناسب است: C و ++C، Perl، Python، Tcl، شل‌اسکریپت یونیکس و بسیاری زبان‌های دیگر.

الگوهای ارسال داده: GET و POST

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

  • GET: داده‌ها به انتهای آدرس URL چسبانده می‌شوند و سرور آن‌ها را در متغیر محیطی QUERY_STRING به برنامه می‌دهد. چون داده در URL دیده می‌شود، در لاگ سرور و تاریخچه‌ی مرورگر هم ثبت می‌شود و برای اطلاعات حساس مناسب نیست. طول URL هم محدودیت دارد؛ این محدودیت را خود پروتکل HTTP تعیین نمی‌کند بلکه به تنظیمات وب‌سرور (مثلاً دستور LimitRequestLine در آپاچی) و سقف مرورگر بستگی دارد و از محیطی به محیط دیگر فرق می‌کند.
  • POST: داده‌ها در بدنه‌ی درخواست قرار می‌گیرند و از طریق ورودی استاندارد به برنامه می‌رسند. برنامه باید مقدار CONTENT_LENGTH را بخواند تا بداند چند بایت از ورودی را باید بردارد. این الگو برای حجم‌های بزرگ‌تر، آپلود فایل و داده‌هایی که نباید در URL ظاهر شوند انتخاب درست‌تری است.

مشکل اصلی CGI: یک پروسه برای هر درخواست

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

چرا CGI کلاسیک مقیاس نمی‌گیرد⁦CGI⁩برای هر درخواست یک پروسه تازه ساخته و بعد نابود می‌شود⁦request⁩⁦fork+exec⁩⁦request⁩⁦fork+exec⁩⁦request⁩⁦fork+exec⁩⁦request⁩⁦fork+exec⁩⁦FastCGI / PHP-FPM⁩استخری از پروسه‌های همیشه زنده، درخواست‌ها را می‌گیرند⁦request⁩⁦request⁩⁦request⁩⁦request⁩استخر پروسه‌های آماده؛ بدون هزینه راه‌اندازی مجددامروز روی میزبانی‌های امروزی، PHP از طریق PHP-FPM یاموتور داخلی کنترل‌پنل اجرا می‌شود و CGI کلاسیک عملاًکنار گذاشته شده است؛ اگر هنوز اسکریپتی در پوشه cgi-binدارید، انتقال آن به FPM هم سریع‌تر است و هم امن‌تر

هزینه واقعی CGI ساختن و از بین بردن یک پروسه به‌ازای هر درخواست است؛ FastCGI و PHP-FPM همان کد را با استخری از پروسه‌های آماده اجرا می‌کنند و همین تفاوت، مقیاس‌پذیری را می‌سازد.

جانشین‌ها: FastCGI، PHP-FPM و ماژول‌های داخلی

راه‌حل‌هایی که بعد از CGI آمدند همگی یک ایده‌ی مشترک دارند: پروسه را زنده نگه دار و درخواست‌ها را به آن بسپار.

روش نحوه‌ی کار
CGI برای هر درخواست یک پروسه‌ی جدید ساخته و بعد بسته می‌شود.
FastCGI چند پروسه‌ی دائمی بالا می‌مانند و سرور درخواست‌ها را از طریق سوکت به آن‌ها می‌دهد.
PHP-FPM پیاده‌سازی FastCGI برای PHP با مدیریت استخر پروسه‌ها، کاربر مجزا برای هر سایت و تنظیمات مستقل.
ماژول داخل سرور مفسر داخل خود وب‌سرور جاسازی می‌شود (مانند mod_php یا mod_perl در آپاچی).

در پنل‌های میزبانی هم همین انتخاب زیر عنوان «PHP Handler» دیده می‌شود و معمولاً گزینه‌هایی مثل CGI، FastCGI و PHP-FPM در دسترس است. امروز روی هاست اشتراکی و سرورهای مدرن، PHP-FPM انتخاب متداول است؛ چون هم پروسه‌ها دائمی‌اند و هم هر سایت با کاربر سیستمی خودش اجرا می‌شود. اگر می‌خواهید خودتان هندلر و نسخه‌ی PHP را انتخاب و تنظیم کنید، به دسترسی ریشه نیاز دارید که روی یک VPS یا سرور اختصاصی فراهم است. تغییر این تنظیمات در محیط گرافیکی هم با پنل‌هایی مثل سی‌پنل انجام می‌شود.

امروز کجا با CGI روبه‌رو می‌شوید؟

CGI برای اپلیکیشن‌های پرترافیک کنار گذاشته شده، اما هنوز از بین نرفته است. پوشه‌ی cgi-bin همچنان در بسیاری از پیکربندی‌های آپاچی وجود دارد. ابزارهای قدیمی‌تر تحلیل لاگ و اسکریپت‌های Perl سازمانی هنوز به این شکل اجرا می‌شوند. Git هم برای سرویس‌دهی مخازن روی HTTP از برنامه‌ای به نام git-http-backend استفاده می‌کند که یک برنامه‌ی CGI است. در دستگاه‌های تعبیه‌شده مانند روتر، دوربین شبکه و پرینتر هم رابط وب معمولاً با اسکریپت‌های کوچک CGI ساخته می‌شود، چون سربار حافظه‌اش ناچیز است و نیازی به فریم‌ورک ندارد.

خطاها و نکات امنیتی رایج

بیشتر خطاهای CGI به قرارداد ساده‌ی «هدر، خط خالی، بدنه» برمی‌گردد. اگر اسکریپت هدر Content-Type را چاپ نکند یا خط خالی بعد از هدرها را جا بیندازد، سرور خطای ۵۰۰ و پیام Premature end of script headers را در لاگ ثبت می‌کند. دو علت متداول دیگر هم وجود دارد: نبود مجوز اجرا روی فایل اسکریپت، و ذخیره‌ی فایل با خط‌پایان ویندوزی (CRLF) که باعث می‌شود مسیر مفسر در خط شبانگ درست خوانده نشود.

از نظر امنیتی هم باید حواستان به همان متغیرهای محیطی باشد. چون هدرهای فرستاده‌شده از سمت کاربر به متغیر محیطی تبدیل می‌شوند، آسیب‌پذیری معروف Shellshock در Bash از همین مسیر روی سرورهای دارای اسکریپت CGI قابل بهره‌برداری بود. به همین ترتیب، ورودی کاربر هرگز نباید مستقیم به دستور شل یا کوئری دیتابیس داده شود. اگر در تشخیص علت خطا یا امن‌سازی یک اسکریپت قدیمی به کمک نیاز داشتید، تیم پشتیبانی فنی می‌تواند لاگ‌های سرور را بررسی کند.

جمع‌بندی

CGI اولین پاسخ استاندارد به این پرسش بود که وب‌سرور چطور یک برنامه را اجرا کند و خروجی‌اش را به کاربر برساند. مدل «یک پروسه برای هر درخواست» برای بار سنگین ساخته نشده بود و امروز جای خود را به FastCGI و به‌طور مشخص PHP-FPM داده است، اما شناختن CGI هنوز ارزش دارد؛ چون تمام جانشینانش دقیقاً همان مفاهیم را با کارایی بهتر پیاده کرده‌اند.