Amirali Yaghoutiمهندس ارشد نرم‌افزار

مطالعه موردی مهندسی

A2 خروجی شبکه فایروال

wp-admin مرتب ده ثانیه یا بیشتر طول می‌کشید تا یک صفحه را رندر کند. علتش دیتابیس یا PHP نبود؛ وردپرس وظیفه‌شناسانه منتظر فراخوان‌های HTTP خروجی به سرورهای لایسنس افزونه‌ها، CDNهای فونت و اندپوینت‌های آپدیت می‌ماند که از جایی که فروشگاه میزبانی می‌شود کند یا اصلاً غیرقابل‌دسترس‌اند.

مسئله تجاری

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

آنچه تحویل دادم

  • a2-egress-firewall-adminspeed.php، یک بلاک‌لیست نه یک اجازه‌لیست، تا با نصب افزونه چیزی که الان کار می‌کند نشکند.
  • فهرستی گزیده از مقاصدی که از این میزبان معلوم است گیر می‌کنند: اندپوینت‌های لایسنس و آپدیت افزونه‌ها، کتابخانه‌های قالب، APIهای فونت و آواتار، و یکی دو CDN.
  • یک مسیر شکست سریع که تایم‌اوت اتصال را به ۱ ثانیه و تایم‌اوت کل را به ۲ ثانیه می‌آورد، برای هرچه صراحتاً بلاک نشده.
  • یک ماژول همراه برای فرانت، a2-egress-assets-firewall.php، که URLهای اسکریپت و استایل خارجی را در script_loader_src و style_loader_src می‌گیرد، با یک حالت پایش که لاگ می‌کند بدون اینکه بلاک کند.
  • یک کلید bypass و یک سوئیچ فقط-ادمین، تا کل ماجرا برای یک درخواست خاص خاموش شود وقتی چیزی واقعاً لازم دارد بیرون برود.

رویکرد فنی

  • بلاک در pre_http_request اتفاق می‌افتد، که یک پاسخ ساختگی برمی‌گرداند قبل از اینکه اصلاً سوکتی باز شود. هیچ‌چیز منتظر نمی‌ماند.
  • تطبیق میزبان بر پایه‌ی پسوند روی هاست پارس‌شده انجام می‌شود نه جست‌وجوی زیررشته در URL، تا یک دامنه‌ی بلاک‌شده نتواند داخل یک query string قاچاق شود.
  • نام‌های میزبان خود سایت و هرچه محلی resolve شود صراحتاً معاف‌اند، چون درخواست‌های loopback همان راهی است که WP-Cron و REST API با خودشان حرف می‌زنند.
  • پیش‌فرض‌ها عمداً محافظه‌کارند: اعمال فقط در ادمین، لاگ خاموش، و فایروال اسِت در حالت پایش عرضه شد تا قبل از تصمیم برای بلاک، لاگ را بخوانم.
  • یک مقصد در سورس مستند شده که عمداً بلاک نشده، چون میزبانی ایرانی است که عادی پاسخ می‌دهد و بلاک‌کردنش یک یکپارچه‌سازی قیمت را می‌شکند.

نتیجه و شواهد

بارگذاری صفحات ادمین دیگر روی فراخوان‌های خروجی گیر نمی‌کند. میانگین تمیز قبل و بعد را اندازه نگرفتم چون تأخیر ذاتاً متغیر بود و به این بستگی داشت که آن روز کدام اندپوینت‌ها در دسترس نیستند؛ نتیجه‌ای که قابل دفاع است این است که خود این حالت شکست از بین رفته، چون مقصدهای بلاک‌شده حالا فوری برمی‌گردند به‌جای اینکه یک تایم‌اوت کامل مصرف کنند.

اهمیت برای کارفرما

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

خلاصه-اجرایی-پروژه

خلاصه اجرایی خوانا

خلاصه_اجرایی {
  پروژه: "A2 Egress Firewall"
  فایل‌ها: "a2-egress-firewall-adminspeed.php (۲۲۴ خط)
            a2-egress-assets-firewall.php (۸۴ خط)"
  راهبرد: "بلاک‌لیست، نه اجازه‌لیست"
  هوک: "pre_http_request → پاسخ ساختگی، بدون سوکت"
  شکست_سریع: "اتصال ۱ثانیه / کل ۲ثانیه برای بقیه"
  دامنه: "فقط wp-admin به‌طور پیش‌فرض؛ کلید bypass موجود"
  فرانت: "script_loader_src + style_loader_src، حالت پایش"
  معاف: "نام‌های میزبان خودی و loopback، همیشه"
}

ارزش حرفه‌ای این پروژه

نسخه‌ی وسوسه‌کننده‌ی این پروژه یک اجازه‌لیست سخت‌گیر است. من بلاک‌لیست را انتخاب کردم چون اجازه‌لیست روی یک فروشگاه زنده با ده‌ها افزونه یک حادثه‌ی تضمین‌شده است، و بلاک‌لیست با کسری از ریسک به بیشتر فایده می‌رسد.

عرضه‌ی نیمه‌ی فرانت در حالت پایش از همین غریزه آمد. ترجیح می‌دهم یک هفته لاگ بخوانم تا حدس بزنم، مخصوصاً وقتی حالت شکست یک استایل‌شیت غایب و نامرئی است.