همه سامانه‌ها عملیاتی هستند 6 منطقه آفشور پرداخت بدون KYC
Hands-on راهنمای میدانی

حملهٔ DDoS به VPS شما: رانبوکِ اولین ساعت

ده دقیقهٔ اول تعیین می‌کند سه ساعت بعدی چطور می‌گذرد، و بیشتر آدم‌ها آن‌ها را با حدس‌زدن تلف می‌کنند — ری‌استارت‌کردنِ سرویس‌ها، بن‌کردنِ آدرس‌ها به‌طور تصادفی، خواندنِ داشبوردی که فقط می‌گوید سایت خوابیده. این همان توالی‌ای است که واقعاً روی ناوگان اجرا می‌کنیم: تأیید کنید حمله واقعی است، شکلش را اندازه بگیرید، باری را که می‌شود از داخل باکس کم کرد کم کنید، و آن نقطهٔ دقیقی را تشخیص دهید که از آن به بعد دیگر هیچ‌چیزی که روی سرور تایپ می‌کنید اهمیتی ندارد و فقط 1.5 Tbps پالایش بالادستیِ جلوی آن اهمیت دارد. نوشته‌شده برای یک VPS از $8.00 در ماه، نه برای یک NOC.

به‌روزرسانی‌شده در 2026-09-06 · 15 دقیقه مطالعه · عملیات ناوگان
در این صفحه
  1. اول، مطمئن شوید که واقعاً حمله است
  2. تریاژِ پنج‌دقیقه‌ای
  3. چیزی که نمی‌توانید از داخلِ باکس درست کنید
  4. لایهٔ 7: سیلی که شبیهِ ترافیک است
  5. چهار کنترل، به‌ترتیبِ تأثیر در هر دقیقه
  6. با آن از ماشینی که داده‌های شما را نگه می‌دارد نجنگید
  7. روزی که شما بازتاب‌دهنده‌اید، نه هدف
  8. بعد از اینکه متوقف شد: پست‌مورتم و جعبه‌ابزارِ همیشه‌آماده
  9. گام‌به‌گام
SP·01

اول، مطمئن شوید که واقعاً حمله است

گران‌ترین اشتباه در یک قطعی، رسیدگی‌کردن به چیز اشتباه است. "سایت کند است" نشانه‌ای است مشترک میان یک سیل واقعی، یک استقرار که یک کوئریِ بدون ایندکس را با خودش آورده، یک cron job که هر ساعت درست سرِ همان دقیقه شروع به خالی‌کردنِ پایگاه‌داده کرده، یک کراولر که جست‌وجوی وجه‌دارِ شما (faceted) را کشف کرده، لینکی که به صفحهٔ اولِ یک سایت بزرگ راه پیدا کرده، و دیسکی که پر شده است. هرکدام از این‌ها از نگاهِ یک مرورگر کاملاً یکسان به‌نظر می‌رسند، و واکنش‌های درست برایشان متقابلاً ناسازگارند: نمی‌خواهید مشتری‌های واقعی را rate-limit کنید چون یک مایگریشن یک ایندکس را جا انداخته.

سه پرسش در کمتر از یک دقیقه این‌ها را از هم جدا می‌کند. آیا حجم ترافیک واقعاً غیرعادی است، یا ترافیک عادی است و این سرور است که کند شده؟ یک سیل خودش را به‌شکل یک جهشِ پلکانی در بسته‌ها یا درخواست‌ها بر ثانیه نشان می‌دهد؛ یک استقرارِ بد، حجمِ درخواستِ عادی و زمانِ پاسخِ فروریخته نشان می‌دهد. آیا چیزی در یک ساعتِ گذشته از سمتِ شما تغییر کرده؟ پیش از فایروال، لاگِ استقرار را چک کنید — روی زیرساخت‌های کوچک، حوادثِ خودزده به‌مراتب از حمله‌ها بیشترند. آیا بار در کل سایت پخش شده، یا روی یک مسیر متمرکز است؟ سیل‌های واقعی معمولاً بی‌تبعیض‌اند یا صفحهٔ اول را هدف می‌گیرند؛ یک endpointِ گران‌قیمت که زیرِ ضربِ صد کلاینت است به سوءاستفاده نزدیک‌تر است تا به یک DDoS، و راه‌حلش هم بسیار ارزان‌تر است.

به این‌ها پاسخ دهید، بعد ادامه دهید. بقیهٔ این راهنما این فرض را دارد که پاسخ این بوده: حجم غیرعادی است، از سمتِ شما چیزی تغییر نکرده، و باکس دارد غرق می‌شود.

SP·02

تریاژِ پنج‌دقیقه‌ای

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

این دو را با مقایسهٔ دو عدد از هم تشخیص دهید. هم‌زمان به شمارنده‌های اینترفیس و تقسیمِ CPU نگاه کنید. اگر بایت‌های rx نزدیکِ سرعتِ پورت‌تان قفل شده‌اند، اگر شمارنده‌های dropped یا overrun بالا می‌روند، و اگر زمان صرفِ soft interrupt می‌شود نه صرفِ اپلیکیشنِ شما، سیل روی لایهٔ 3 یا 4 است و یک مسئلهٔ ظرفیت است. اگر پهنای‌باند چیزِ غیرعادی‌ای نشان نمی‌دهد اما استخرِ workerها اشباع شده، اتصال‌ها صف کشیده‌اند، و لاگِ access پر از درخواست‌های به‌ظاهر معتبر است، این لایهٔ 7 است و یک مسئلهٔ فیلترینگ است.

سپس موردِ لایهٔ 3/4 را یک قدم جلوتر دسته‌بندی کنید، چون زیرگونه‌ها رفتارِ متفاوتی دارند. یک سیلِ SYN به‌شکلِ ده‌ها هزار سوکتِ نیمه‌باز در SYN-RECV ظاهر می‌شود؛ کرنل وقتی syncookie روشن باشد این را خوب مدیریت می‌کند. یک سیلِ UDP یا تقویت‌شده (amplification) به‌شکلِ حجمِ ورودیِ عظیم روی پورت‌هایی ظاهر می‌شود که حتی به آن‌ها گوش هم نمی‌دهید — بازتاب‌های DNS، NTP، memcached، CLDAP — و هیچ‌چیزی که خودتان اجرا کنید کمکی نمی‌کند، چون آسیب تا زمانی که بسته‌ها به NIC شما برسند از قبل واردشده. یک سیلِ فرگمنتیشن یا بستهٔ خام به‌شکلِ packets-per-second بالا با پهنای‌باندِ متوسط ظاهر می‌شود، که CPU را گرسنه نگه می‌دارد نه لینک را. پیش از دست‌زدن به فایلِ کانفیگ، بنویسید کدام‌یک را دارید.

SP·03

چیزی که نمی‌توانید از داخلِ باکس درست کنید

این همان بخشی است که بیشترِ مقاله‌ها از آن می‌گذرند، و همان بخشی است که تعیین می‌کند ساعتِ شما مفید بوده یا نه. یک قاعدهٔ فایروال روی هدف، یک آپلینکِ اشباع‌شده را نجات نمی‌دهد. DROPِ iptablesِ شما روی ماشینِ انتهای لوله اجرا می‌شود — بسته از قبل از لینکِ ترانزیت گذشته، از قبل پهنای‌باندی را که پولش را می‌دهید مصرف کرده، و از قبل بستهٔ یک کاربرِ واقعی را جابه‌جا کرده است. دراپ‌کردنِ محلیِ آن، اپلیکیشنِ شما را از هدررفتنِ سیکل‌ها حفظ می‌کند، که ارزشش را دارد، اما پهنای‌باندِ شما را از هیچ‌چیز حفظ نمی‌کند.

سقفِ صادقانه برای یک سرورِ تنها، تقریباً کوچک‌ترینِ دو عدد است: سرعتِ پورتی که به آن وصل است، و تعدادِ بسته‌بر‌ثانیه‌ای که CPUاش می‌تواند دسته‌بندی کند. یک پورتِ 1 Gbps در 1 Gbps پر می‌شود، فارغ از اینکه ruleset شما چقدر شیک باشد، و یک سیلِ نه‌چندان بزرگ از بسته‌های کوچک می‌تواند چند هسته را روی مدیریتِ interrupt خسته کند، خیلی پیش از آنکه عددِ پهنای‌باند نگران‌کننده به نظر برسد. پس از آن نقطه، تنها چیزی که کمک می‌کند دستگاهی است جلوترِ بالادست که ظرفیتش از حمله بیشتر است و ترافیک را پیش از آنکه اصلاً به لینکِ شما برسد، دراپ می‌کند. اسکرابینگ (scrubbing) دقیقاً همین است، و به همین دلیل است که تک‌تکِ ماشین‌های ناوگانِ ما پشتِ 1.5 Tbps کاهشِ حملهٔ همیشه‌روشن قرار دارند، نه پشتِ یک فایروالِ بزرگ‌تر.

نتیجه‌اش هم به همان اندازه اهمیت دارد: اگر هیچ محافظتِ بالادستی نداشته باشید، واکنشِ ارائه‌دهندهٔ شما به یک حملهٔ حجمیِ بزرگ این است که آدرسِ شما را null-route کند، چون جایگزین، افت‌دادنِ کیفیتِ همهٔ مشتری‌های دیگرِ روی همان لینک است. این بدجنسی نیست، ریاضی است — و یعنی مهاجم با گران‌کردنِ شما برنده می‌شود، نه با شکستنِ چیزی. از پیش دانستنِ اینکه آیا کاهشِ حمله در پلنِ شما گنجانده شده یا یک افزودنیِ پولی است که هرگز فعالش نکرده‌اید، یک بررسیِ پنج‌دقیقه‌ای است که ارزشش را دارد همین امروز انجامش دهید، نه وسطِ حادثه. مالِ ما روی هر پلنی گنجانده شده، که تنها ترتیبی است که ساعتِ سه صبح کمک می‌کند.

SP·04

لایهٔ 7: سیلی که شبیهِ ترافیک است

سیلِ لایهٔ اپلیکیشن سخت‌تر است چون تک‌تکِ درخواست‌ها به‌تنهایی مشروع‌اند. یک سیلِ HTTP خوب‌ساخته، دست‌دادنِ TCP را کامل می‌کند، TLS را مذاکره می‌کند، یک GET / معتبر با یک user agentِ باورپذیر می‌فرستد، و پاسخ را می‌خواند. هیچ بسته‌ای در آن بدشکل نیست. چیزی که شما را می‌کشد ریاضی است: فرستادنِ یک درخواست تقریباً هیچ هزینه‌ای برای مهاجم ندارد، اما برای شما هزینهٔ یک کوئریِ پایگاه‌داده، یک رندرِ template، و صد میلی‌ثانیه از یک workerی دارد که دیگر به هیچ‌کسِ دیگری سرویس نمی‌دهد.

نشانه‌ها در همان لاگِ accessِ خودتان‌اند، و معمولاً وقتی دنبالشان بگردید، نه وقتی فقط نگاهشان کنید، آشکارند. رشته‌های کوئریِ cache-buster — هزاران درخواست به /?1234567، هرکدام یک URLِ یکتا که هر کشی که دارید را دور می‌زند — رایج‌ترین امضای تنها هستند. توزیعِ user-agentی بدون دنبالهٔ بلند: ترافیکِ واقعی آمیزه‌ای شلوغ از صدها نسخهٔ مرورگر است، و یک سیل اغلب سه رشته است که میلیون‌بار تکرار شده، یا رشته‌ای که هیچ کاربرِ واقعی‌ای از آن استفاده نمی‌کند. فیلدِ referrerی که همه‌جا یکسان است. درخواست‌هایی که کلاً از کنارِ static assetهای شما رد می‌شوند — یک مرورگرِ واقعی بعد از HTML، CSS، فونت‌ها و تصویرها را می‌گیرد؛ یک کلاینتِ سیل فقط HTML را می‌خواهد و می‌رود. و توزیعِ منبعی که بیش‌ازحد صاف است: یک بات‌نتِ پخش‌شده روی ده‌هزار آدرسِ خانگی که هرکدام دو درخواست در ثانیه می‌فرستند، تا وقتی متوجه نشوید نرخِ به‌ازای هر آدرس به‌طرزِ مشکوکی یکنواخت است، شبیهِ محبوبیت به‌نظر می‌رسد.

بعد یک گونهٔ دیگر هم هست که تقریباً به هیچ ترافیکی نیاز ندارد: حملهٔ کند (slow attack). چند صد اتصال که باز می‌شوند، هر بیست ثانیه یک هدر می‌فرستند و هرگز تمام نمی‌شوند، تمامِ workerهایی را که دارید اشغال می‌کنند درحالی‌که نمودارِ پهنای‌باندِ شما صاف می‌ماند. راه‌حل یک rate limit نیست — نرخِ درخواست ناچیز است — بلکه timeoutهای تهاجمی برای هدر و بدنه است، و دقیقاً به همین دلیل است که آن‌ها در اولین بلوکِ کانفیگِ زیر می‌آیند، نه به‌عنوانِ فکرِ بعدی.

SP·05

چهار کنترل، به‌ترتیبِ تأثیر در هر دقیقه

زیرِ فشار، اول کاری را انجام دهید که بیشترین اهرم را دارد. ترتیبِ زیر دلبخواهی نیست؛ تقریباً نزولی است بر اساسِ اینکه هرکدام به‌ازای هر دقیقه از توجهِ شما چقدر بار را کم می‌کند.

  • چیزِ ارزانی سرو کنید. یک میکروکشِ سی‌ثانیه‌ای جلوی اپلیکیشنِ شما، هزار درخواستِ یکسان در ثانیه را به یک برخوردِ origin و 999 خواندنِ حافظه تبدیل می‌کند. این بزرگ‌ترین اهرمِ تنها روی تقریباً هر سیلِ HTTP است، هزینه‌اش یک بلوکِ directive است، و برای ترافیکِ ناشناس تقریباً همیشه امن است. proxy_cache_lock را اضافه کنید تا یک cache miss یک گلهٔ رمنده (thundering herd) به سمتِ بک‌اند نفرستد.
  • هم‌زمانی را سقف بگذارید و timeoutها را کوتاه کنید. limit_conn به‌ازای هر آدرس به‌علاوهٔ timeoutهای سفت‌وسختِ هدر، بدنه و keepalive، حملاتِ کند را کاملاً می‌کشد و جلوی هر کلاینتِ تکی را می‌گیرد که استخرِ workerِ شما را پارک کند. این کنترلی است که کمترین هزینه را برای کاربرانِ واقعی دارد.
  • Rate-limit کنید، با یک burst. limit_req با یک burstِ معقول دقیق است اما تنظیمش کندتر است، و همان کنترلی است که اگر آن را از سرِ وحشت تنظیم کنید نه از سرِ baselineِ خودتان، false positive تولید می‌کند. پیش از آنکه بتوانید عددی انتخاب کنید باید نرخِ عادیِ درخواست‌بر‌ثانیهٔ به‌ازای هر کلاینت را بدانید — و دقیقاً به همین دلیل است که پست‌مورتمِ انتهای این راهنما بیش از آن‌چه به‌نظر می‌رسد اهمیت دارد.
  • مسدود کنید، محدود و با اکراه. دراپ‌کردنِ شبکه‌های مشخص وقتی منابع متمرکزند جواب می‌دهد و وقتی متمرکز نیستند هیچ فایده‌ای ندارد. همچنین بد پیر می‌شود: هر بلاکی که در طولِ یک حادثه اضافه می‌کنید، ممکن است سه ماه بعد مشتری‌ای باشد که بی‌سروصدا ردش می‌کنید. از یک ست با timeout استفاده کنید تا قانون‌ها خودشان منقضی شوند.

توجه کنید چه چیزی در این فهرست نیست: بن‌کردنِ دستیِ آدرس‌های IP تک‌تک، ری‌استارت‌کردنِ مکررِ وب‌سرور، و غیرفعال‌کردنِ فایروال برای "دیدنِ اینکه کمک می‌کند یا نه". اولی برای اینکه در برابرِ یک منبعِ توزیع‌شده اثری داشته باشد بیش‌ازحد کند است، دومی هر اتصالِ گرمی را که داشتید دور می‌ریزد، و سومی همان راهی است که یک حادثه به یک نفوذ (compromise) تبدیل می‌شود.

SP·06

با آن از ماشینی که داده‌های شما را نگه می‌دارد نجنگید

هرکدام از کنترل‌های بالا وقتی جایی غیر از باکسِ نگه‌دارندهٔ پایگاه‌دادهٔ شما اجرا شود، ارزشِ بیشتری دارد. اگر لبهٔ شما (edge) یک نودِ جداست، سیل روی ماشینی پایان می‌یابد که کارِ تمام‌وقتش پایان‌دادن به سیل‌هاست: کش می‌کند، rate-limit می‌کند و با آدرسِ واقعیِ کلاینت در دست، دراپ می‌کند، و مبدأ (origin) فقط باقیماندهٔ کوچک و فیلترشده را روی یک تونلِ خصوصی می‌بیند. وقتی لبه از پا بیفتد، آن را در 15 min جایگزین می‌کنید و هیچ‌چیز از دست نمی‌دهید، چون هیچ‌چیزی رویش نیست. وقتی مبدأ از پا بیفتد، یک قطعی و یک ریستور در پیش دارید.

این جداسازی، همان دورزدنی را هم می‌بندد که بیشترِ کاهشِ حمله‌ها را تزئینی می‌کند. اگر مبدأ همچنان یک شنوندهٔ عمومی داشته باشد، مهاجمی که آدرسش را پیدا کند — از طریقِ DNS منفعل، یک ورودیِ certificate transparency، یک رکوردِ MX یا یک پیش‌نمایشِ لینک — می‌تواند از کنارِ هر کنترلی که پیکربندی کرده‌اید نشانه برود و مستقیم به اپلیکیشن بزند. این آن‌قدر رایج است که ارزش دارد تا خلافش ثابت نشود، آن را حالتِ پیش‌فرضِ هر سایتِ "محافظت‌شده‌ای" در نظر بگیرید. ساختنِ نسخه‌ای که دوام می‌آورد خودش یک راهنمای جداست: یک reverse proxyِ آفشور بدون هیچ شنوندهٔ عمومی روی مبدأ.

یک هشدار دربارهٔ وسطِ یک حادثه: این معماری است، نه کمکِ اولیه. برپاکردنِ یک لبه، جابه‌جاکردنِ DNS و بازسازیِ یک تونل درحالی‌که زیرِ حمله‌اید، کاری دوساعته است که زیرِ فشار بد انجام می‌شود، و فقط تغییرِ DNS هم به‌اندازهٔ همان TTLای که دارید اثر نمی‌کند. اگر دارید، از آن استفاده کنید. اگر ندارید، ساعت را با کنترل‌هایی که دارید پشتِ سر بگذارید، و آن را در هفتهٔ آرامِ بعدی بسازید — که دقیقاً همان زمانی است که هیچ‌کس این کار را نمی‌کند.

SP·07

روزی که شما بازتاب‌دهنده‌اید، نه هدف

یک نسخهٔ دومِ این حادثه هم هست که در آن سرورِ شما قربانی نیست و هیچ‌کس هم به شما نمی‌گوید. یک ریزالورِ باز، یک دیمنِ NTPِ درمعرض، یک memcachedِ بدونِ احراز هویت روی یک اینترفیسِ عمومی، یک پاسخ‌دهندهٔ SSDP یا CLDAP داخلِ یک کانتینر — هرکدام از این‌ها به یک درخواستِ کوچکِ جعلی، پاسخی بسیار بزرگ‌تر می‌دهد که به سمتِ کسِ دیگری نشانه رفته. از سمتِ شما، نشانه‌ها وارونه‌اند: پهنای‌باندِ خروجی بالاست، ورودی متوسط است، اپلیکیشنِ شما خوب کار می‌کند، و اولین سیگنالِ واقعی یک اخطارِ سوءاستفاده یا یک پورتِ معلق‌شده است.

این بررسی یک دقیقه طول می‌کشد و جایش در همان رانبوک است، چون همان دستوری است که در طولِ تریاژ از قبل اجرایش کرده‌اید. ss -tulpn باید هیچ‌چیزی را که خودتان عامدانه آنجا نگذاشته‌اید، روی یک آدرسِ عمومی بایندشده نشان ندهد، و سرویس‌های UDP سزاوارِ شکِ ویژه‌ای هستند چون همان‌هایی‌اند که تقویت می‌کنند. یک ریزالورِ recursive باید فقط به localhost یا یک آدرسِ تونل بایند شده باشد؛ memcached و Redis اصلاً نباید هرگز از اینترنت قابل‌دسترس باشند؛ و هر کانتینری که با -p 0.0.0.0: یک پورت را منتشر کند، درست یک سوراخ در فایروالی که پیکربندی کرده بودید ایجاد کرده، چون Docker قوانینِ خودش را جلوترِ از قوانینِ شما می‌نویسد. آن موردِ آخر هر بار همه را غافلگیر می‌کند.

همین الگو، سیل‌های خروجی از ماشینی را هم پوشش می‌دهد که از قبل نفوذ به آن انجام شده، که دلیلِ دیگرِ null-routeکردنِ ناگهانیِ یک آدرس توسطِ یک ارائه‌دهنده است. اگر نمودارِ خروجیِ شما بالاست و اپلیکیشنِ شما بی‌کار است، خواندنِ کانفیگ را متوقف کنید و بررسیِ پردازه‌ها را شروع کنید — آن یک نفوذ است، نه یک مسئلهٔ ظرفیت، و واکنشِ درست بازسازیِ باکس از یک بک‌آپِ known-good است، نه فیلترکردنش.

SP·08

بعد از اینکه متوقف شد: پست‌مورتم و جعبه‌ابزارِ همیشه‌آماده

حمله‌ها متوقف می‌شوند. معمولاً مهاجم حوصله‌اش سر می‌رود، گاهی کاهشِ حمله آن را بی‌فایده می‌کند، و گاهی هم صرفاً یک اشتراکِ booterِ با مدتِ ثابت بوده که تمام شده. وسوسه در آن لحظه این است که همه‌چیز را دقیقاً همان‌طور که هست رها کنید و بروید بخوابید، و دقیقاً همین‌طور است که یک rate limitِ موقت، نه ماه بعد، به یک 429ِ دائمی، فراموش‌شده و بی‌صدا برای یک کشورِ کامل تبدیل می‌شود. بیست دقیقه وقت بگذارید و حلقه را ببندید، حالا که تازه است.

ارزش دارد سه مصنوع (artefact) تولید کنید. یک baseline: درخواست‌های عادیِ شما بر ثانیه، نرخِ عادیِ به‌ازای هر کلاینت، پهنای‌باندِ عادیِ شما در اوج. بدونِ این اعداد، هر محدودیتی که در حادثهٔ بعدی تنظیم کنید یک حدس است، و نیمی از آن‌ها در همان جهتی اشتباه خواهند بود که به مشتری‌ها آسیب می‌زند. یک فهرستِ بازگردانی (revert): هر چیزی که تغییر داده‌اید، همراه با تاریخ و دلیل، تا کانفیگِ اضطراری بی‌سروصدا به کانفیگِ دائمی تبدیل نشود. یک رانبوک که چهار دستور طول دارد و جایی زندگی می‌کند که وقتی سایت خواب است بتوانید بهش دسترسی داشته باشید — نه روی سرور، و نه فقط توی سرِ خودتان.

سپس دو شکافِ ساختاری را ببندید، چون این لایه دقیقاً همین‌جای پشته می‌نشیند. کاهشِ حمله همان چیزی است که حجم را جذب می‌کند، یک لبه همان چیزی است که سیل را از داده‌های شما دور نگه می‌دارد، و باکسِ پشتِ این‌ها هنوز هم باید درست‌وحسابی سخت‌سازی شده باشد و بک‌آپ‌های قابل‌بازیابیِ خارج از سایت داشته باشد، چون حادثهٔ بعد از این ممکن است اصلاً یک سیل نباشد. سروری که از یک DDoS جان سالم به‌در می‌برد و یک ماه بعد دیسکش را از دست می‌دهد، هرگز تاب‌آور نبوده — دو بار شانس آورده بوده.

SP·09

گام‌به‌گام

  1. 01

    پیش از آنکه چیزی را تغییر دهید، تأیید کنید

    پیش از دست‌زدن به یک فایلِ کانفیگ، یک تصویرِ صادقانه از ماشین به‌دست بیاورید. دنبالِ یک جهشِ پلکانی در بسته‌ها یا درخواست‌ها می‌گردید، و دنبالِ اینکه زمان کجا صرف می‌شود — اپلیکیشنی که soft interruptها CPUاش را گرسنه نگه داشته‌اند، حادثه‌ای کاملاً متفاوت است از اپلیکیشنی که منتظرِ یک پایگاه‌داده است.

    # is the box alive, and where is the time going?
    uptime                     # load average against your core count
    vmstat 1 5                 # 'in' and 'cs' high, 'id' near zero = packet work
    mpstat -P ALL 1 3          # %soft pinned on one core = interrupt saturation
    
    # is the pipe full, or just busy?
    ip -s link show eth0       # rx bytes, and the errors/dropped counters
    ethtool eth0 | grep -i speed
    
    # requests per second from your own log, minute by minute
    tail -n 20000 /var/log/nginx/access.log \
      | awk -F'[][]' '{print $2}' | cut -d: -f2-4 | uniq -c | tail -5

    پیش از آنکه نتیجه بگیرید حمله است، لاگِ استقرارِ خودتان و جدولِ cronِ خودتان را چک کنید. قطعی‌های خودزده روی یک VPSِ تنها از سیل‌ها رایج‌ترند، و از بیرون یک شکل به‌نظر می‌رسند.

  2. 02

    شکلِ ترافیک را در شصت ثانیه اندازه بگیرید

    حالا دسته‌بندی‌اش کنید. سه پرسش: چند منبعِ متمایز، چه پروتکل و چه وضعیتی، و — اگر HTTP است — کدام مسیرها و کدام agentها. پاسخ‌ها تعیین می‌کنند سراغِ کدام کنترل بروید، و جمع‌آوری‌شان حدود یک دقیقه طول می‌کشد.

    # top source addresses on the wire right now
    timeout 20 tcpdump -nn -i eth0 -c 20000 2>/dev/null \
      | awk '{print $3}' | rev | cut -d. -f2- | rev \
      | sort | uniq -c | sort -rn | head -20
    
    # TCP state census: a wall of SYN-RECV is a SYN flood
    ss -tan | awk '{print $1}' | sort | uniq -c | sort -rn
    
    # layer 7: talkers, paths, agents over the last 50k requests
    L=/var/log/nginx/access.log
    tail -n 50000 $L | awk '{print $1}' | sort | uniq -c | sort -rn | head -20
    tail -n 50000 $L | awk '{print $7}' | sort | uniq -c | sort -rn | head -20
    tail -n 50000 $L | cut -d'"' -f6   | sort | uniq -c | sort -rn | head -10

    نتیجه را در برابرِ نشانه‌ها بخوانید: رشته‌های کوئریِ یکتا روی یک مسیر، فهرستِ user-agentی بدون دنبالهٔ بلند، هیچ درخواستی برای static assetهای شما، یا نرخی به‌ازای هر آدرس که به‌طرزِ عجیبی یکنواخت است. اگر پهنای‌باند روی پورت‌هایی بالاست که به آن‌ها گوش نمی‌دهید، همین‌جا متوقف شوید — آن یک سیلِ حجمی است و مرحلهٔ شش تنها مرحله‌ای است که اهمیت دارد.

  3. 03

    چیزِ ارزانی سرو کنید، و اتصال‌ها را سقف بگذارید

    اول بیشترین اهرم. یک میکروکشِ سی‌ثانیه‌ای، سیلی از درخواست‌های ناشناسِ یکسان را به یک برخوردِ origin فروپاشیده می‌کند، و timeoutهای سفت‌وسخت حملاتِ کند را می‌کشند که یک rate limit نمی‌تواند ببیندشان. هر دو را در بلوکِ http بگذارید، بعد reload کنید نه restart، تا اتصال‌های گرمِ خود را نگه دارید.

    # /etc/nginx/nginx.conf — http block
    limit_conn_zone $binary_remote_addr zone=perip:10m;
    limit_req_zone  $binary_remote_addr zone=flood:20m rate=10r/s;
    
    client_header_timeout 10s;
    client_body_timeout   10s;
    send_timeout          10s;
    keepalive_timeout     20s;
    reset_timedout_connection on;
    
    proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=hot:64m
                     max_size=2g inactive=10m use_temp_path=off;
    # the server block — cap concurrency, serve the cached copy
    limit_conn perip 20;
    
    location / {
        proxy_cache hot;
        proxy_cache_valid 200 301 302 30s;
        proxy_cache_lock on;
        proxy_cache_use_stale error timeout updating
                              http_500 http_502 http_503 http_504;
        add_header X-Cache $upstream_cache_status;
        proxy_pass http://127.0.0.1:8080;
    }
    nginx -t && systemctl reload nginx
    curl -sI https://example.com/ | grep -i x-cache   # want: HIT on the second call
  4. 04

    کلاینتِ واقعی را rate-limit کنید، نه پروکسیِ خودتان را

    اگر چیزی جلوی nginx نشسته باشد، هر درخواست از یک آدرس می‌رسد و یک محدودیتِ به‌ازای هر آدرس، به‌جای مهاجم، پروکسی را throttle می‌کند — یا کلاً بنش می‌کند و سایت را درست وقتی از آن دفاع می‌کنید می‌خواباند. فقط به هدرِ forward‌شده از آدرسِ پروکسی اعتماد کنید، هرگز از اینترنت، بعد محدودیت را اعمال کنید.

    # /etc/nginx/conf.d/realip.conf — the tunnel or edge address only
    set_real_ip_from 10.66.0.1;
    real_ip_header   X-Forwarded-For;
    real_ip_recursive off;
    # burst absorbs bursty humans; nodelay keeps the page fast for them
    location / {
        limit_req zone=flood burst=20 nodelay;
        limit_req_status 429;
    }
    
    # the expensive paths get a much tighter bucket of their own
    location ~ ^/(search|login|register|api/) {
        limit_req zone=flood burst=5;
        limit_req_status 429;
    }

    بعد ببینید همین الان چه کردید: tail -f /var/log/nginx/error.log | grep limiting. اگر آدرس‌هایی که محدود می‌شوند شبیهِ مشتری‌های شما به‌نظر می‌رسند، نرخ خیلی پایین است — بالایش ببرید. محدودیتی که کاربرانِ واقعی را می‌بندد، قطعی‌ای است که خودتان ایجادش کرده‌اید.

  5. 05

    محدود مسدود کنید، و به هر بلاک یک انقضا بدهید

    فقط وقتی ارزشِ انجام‌دادن دارد که مرحلهٔ دو منابعِ متمرکز نشان داده باشد. از یک ست استفاده کنید، نه هزار قانون — یک lookupِ ipset زمانِ ثابت دارد، یک زنجیرهٔ بلندِ iptables برای هر بسته پیموده می‌شود و خودش تبدیل به یک denial of service می‌شود. به هر ورودی یک timeout بدهید تا اضطرارِ امروز به فهرستِ بلاکِ بی‌صدای سالِ بعد تبدیل نشود.

    ipset create flood hash:net timeout 3600 -exist
    iptables -I INPUT -m set --match-set flood src -j DROP
    
    # feed it from the census: /24s you actually verified, not guesses
    for n in 203.0.113.0/24 198.51.100.0/24; do ipset add flood $n -exist; done
    
    # SYN flood: let the kernel do the part it is good at
    sysctl -w net.ipv4.tcp_syncookies=1
    sysctl -w net.ipv4.tcp_max_syn_backlog=8192
    sysctl -w net.core.somaxconn=8192
    
    ipset list flood | head -20      # keep a copy of this for the post-mortem

    مقاومت کنید در برابرِ geo-block‌کردنِ یک کشورِ کامل، مگر آنکه بتوانید نامِ مشتری‌هایی را که قطع می‌کنید ببرید. و هرگز برای آزمایشِ یک فرضیه ufw disable نکنید: یک باکسِ بدونِ فایروال زیرِ حملهٔ فعال، دقیقاً همان راهی است که یک حادثهٔ پهنای‌باند به یک نفوذ (breach) تبدیل می‌شود.

  6. 06

    به لایه‌ای اسکالیت کنید که واقعاً می‌تواند آن را جذب کند

    اگر شمارنده‌های اینترفیس می‌گویند لینک اشباع شده، یا drops درحالی‌که CPUِ شما بی‌کار است بالا می‌رود، شما به سقفِ هر کاری که می‌توانید روی سرور انجام دهید رسیده‌اید. آن قرائت را تأیید کنید، بعد به‌جای ادامهٔ تنظیم، اسکالیت کنید.

    # drops in the stack itself — second column is 'dropped'
    awk '{print strtonum("0x" $2)}' /proc/net/softnet_stat | paste -sd+ | bc
    
    # interface-level loss against link speed
    ip -s link show eth0 | sed -n '3,6p'

    وقتی اسکرابینگِ بالادستی برقرار باشد، معمولاً کاری برای انجام‌دادن نیست: تشخیص همیشه‌روشن است و سیل در شبکه، پیش از آنکه به پورتِ شما برسد، دراپ می‌شود — روی ناوگانِ ما این 1.5 Tbps ظرفیت است که جلوی هر پلنی نشسته، پس حادثه اغلب فقط بعداً و به‌شکلِ یک نمودار قابل‌دیدن است. اگر حمله یک سیلِ HTTPِ خوب‌ساخته باشد نه یک سیلِ حجمی، این همان موردی است که یک سپرِ L7 برایش ساخته شده، چون سیل‌های درخواست در سطحِ بسته از کاربرها قابل‌تشخیص نیستند و باید بالاتر از آن قضاوت شوند. اگر اصلاً هیچ کاهشِ حمله‌ای ندارید، گزینه‌های واقع‌بینانهٔ شما این‌اند که پشتِ لبه‌ای بروید که دارد، یا صبر کنید — و اولی را پیش از حملهٔ بعدی برنامه‌ریزی کنید.

  7. 07

    حلقه را ببندید: تأیید کنید، برگردانید، بعد بنویسیدش

    از بیرونِ ماشین تأیید کنید، نه از یک شلِ رویش. بعد اقداماتِ اضطراری را عامدانه واگردانید، آن‌هایی را که همیشه ایدهٔ خوبی بودند نگه دارید، و عددها را ثبت کنید تا حادثهٔ بعدی از یک baseline شروع شود نه از یک حدس.

    # from somewhere else entirely: is the site healthy for a normal user?
    curl -s -o /dev/null -w 'code=%{http_code} ttfb=%{time_starttransfer}s\n' \
         https://example.com/
    
    # did you leave a limit that is biting real people?
    grep -c 'limiting requests' /var/log/nginx/error.log
    
    # what is still blocked, and when does it expire?
    ipset list flood | head -30

    کش، timeoutها و syncookieها را نگه دارید — این‌ها بهبودهای دائمی‌اند. rate limitهای تهاجمی را به baselineِ اندازه‌گیری‌شدهٔ خودتان به‌علاوهٔ کمی فضا برگردانید، و بگذارید ورودی‌های ipset خودشان منقضی شوند. بعد چهار دستورِ مرحله‌های یک و دو را در یک رانبوک بنویسید که جایی غیر از همین سرور نگه‌داری می‌شود، کنارِ نرخِ عادیِ درخواست‌بر‌ثانیه و پهنای‌باندِ اوجِ عادیِ خودتان. حملهٔ بعدی صرفاً به این دلیل که این دو عدد وجود دارند، حادثه‌ای کوتاه‌تر خواهد بود.

SP·10 — پرسش‌های متداول

پاسخ‌های سریع

چطور یک DDoSِ واقعی را از یک جهشِ ترافیکی تشخیص بدهم؟

به شکل نگاه کنید نه به اندازه. جهش‌های ارگانیک ساختار دارند: از شبکه‌های زیادی می‌آیند، user agentها یک دنبالهٔ بلندِ شلوغ‌اند، کلاینت‌ها بعد از HTML سراغِ CSS و تصویرهای شما می‌روند، و referrer معمولاً به یک جای واقعی اشاره می‌کند. یک سیل صاف و تکراری است — همان مشتِ کوچکِ agentها، هیچ درخواستی برای static asset، نرخ‌های یکنواخت به‌ازای هر کلاینت، و اغلب رشته‌های کوئریِ یکتا که برای دورزدنِ کش طراحی شده‌اند. دو چیز هم خیلی زود خودشان را کنار می‌گذارند: پیش از آنکه کسی را مقصر بدانید، لاگِ استقرار و جدولِ cronِ خودتان را چک کنید، چون یک کوئریِ کندِ یک‌ساعت‌پیش‌منتشرشده هم دقیقاً همین نشانه‌ها را تولید می‌کند و به راه‌حلِ برعکس نیاز دارد.

آیا iptables یا fail2ban می‌توانند جلوی یک DDoS را بگیرند؟

در برابرِ حملاتِ کوچک و متمرکز کمک می‌کنند، و در برابرِ حملاتِ حجمی هیچ فایده‌ای ندارند. هر دو روی ماشینِ انتهای لوله اجرا می‌شوند، پس تا زمانی که یک قانون فایر شود، بسته از قبل پهنای‌باندِ شما را مصرف کرده — شما دارید سیکل‌های CPU را ذخیره می‌کنید، نه ظرفیتِ لینک را. fail2ban هم طراحی‌اش واکنشی است: یک لاگ را بعد از وقوع می‌خواند، که برای یک سیل چند دقیقه‌ای خیلی دیر است، و اگر آدرسِ واقعیِ کلاینت را پیکربندی نکرده باشید می‌تواند reverse proxyِ خودتان را بن کند. از آن‌ها به‌عنوانِ بهداشت در برابرِ brute force و اسکنرها استفاده کنید؛ در برابرِ حجم از اسکرابینگِ بالادستی استفاده کنید.

آیا تغییرِ آدرسِ IPِ سرورم کمک می‌کند؟

به‌طورِ کوتاه و به‌ندرت، و هزینه‌اش این است که هر کشِ DNSای در دنیا را از دست می‌دهید. اگر مهاجم یک hostname را هدف گرفته، آدرسِ جدید ظرفِ چند دقیقه در DNS قرار می‌گیرد و سیل دنبالش می‌رود؛ این حرکت فقط وقتی چیزی به شما می‌دهد که هدف، آدرسِ خام باشد و مهاجم راهِ آسانی برای re-resolveکردنش نداشته باشد. نسخه‌ای که واقعاً کار می‌کند ساختاری است نه ظاهری: یک لبهٔ قابل‌جایگزین جلو بگذارید، مبدأ را از اینترنتِ عمومی غیرقابل‌دسترس نگه دارید، و آن‌وقت آدرسی که زیرِ حمله است همان چیزی می‌شود که می‌توانید در 15 min بدونِ دست‌زدن به داده‌هایتان دورش بیندازید.

آیا کاهشِ DDoS تأخیر اضافه می‌کند یا چیزی را خراب می‌کند؟

اسکرابینگِ لایهٔ 3/4 همیشه‌روشن است و عملاً نامرئی — بسته‌ها از یک مسیرِ فیلترینگ عبور می‌کنند به‌جای آنکه فقط در طولِ یک رویداد منحرف شوند، پس نه لحظهٔ failoverای هست و نه جریمهٔ قابل‌اندازه‌گیری‌ای روی ترافیکِ عادی. فیلترینگِ لایهٔ اپلیکیشن فرق دارد، چون قضاوت دربارهٔ اینکه یک درخواست انسانی هست یا نه، گاهی یعنی به‌چالش‌کشیدنِ آن، و هر چالشی روی تعدادِ کمی از کلاینت‌های غیرعادی اثر می‌گذارد. دو حالتِ شکستی که ارزشِ برنامه‌ریزی دارند، پاسخ‌های کش‌شده‌ای هستند که به کاربرانِ واردشده سرو می‌شوند، که یک مسئلهٔ cache-key است نه یک مسئلهٔ کاهشِ حمله، و کلاینت‌های APIای که نمی‌توانند به یک چالش پاسخ دهند، که دقیقاً به همین دلیل است که مسیرهای APIِ خودتان را عامدانه معاف می‌کنید، نه اینکه بعداً کشفش کنید.

آیا ممکن است سرورِ من همان چیزی باشد که به کسِ دیگری حمله می‌کند؟

این بیش از آن‌چه مردم انتظار دارند رخ می‌دهد، و نشانه‌ها وارونه‌اند: پهنای‌باندِ خروجیِ بالا، ورودیِ عادی، اپلیکیشنی که حالش خوب به‌نظر می‌رسد، و یک اخطارِ سوءاستفاده به‌عنوانِ اولین سیگنالِ واقعی. دلایلِ معمول یک ریزالورِ DNSِ باز، یک سرویسِ NTP یا memcachedِ درمعرض، یا کانتینری هستند که یک پورت را روی 0.0.0.0 منتشر کرده و قانونِ فایروالِ خودش را جلوترِ از قانونِ شما نوشته. ss -tulpn را اجرا کنید و مطمئن شوید چیزی که قصدش را نداشته‌اید به یک آدرسِ عمومی بایند نیست. اگر خروجی بالاست و هیچ سرویسِ مشروعی توضیحش نمی‌دهد، آن را یک نفوذ در نظر بگیرید و از یک بک‌آپِ known-good بازسازی کنید، نه اینکه سعی کنید با فیلترکردن از آن خلاص شوید.

محافظتِ L3/4 گنجانده شده — آیا هنوز به یک سپرِ L7 نیاز دارم؟

کاملاً به این بستگی دارد که چه چیزی اجرا می‌کنید. یک سایتِ استاتیک، یک گیم‌سرور یا هر چیزی که بارش بسته است نه رندرِ صفحه، به‌تنهایی با اسکرابینگِ سطحِ شبکه به‌خوبی پوشش داده می‌شود. یک اپلیکیشنِ داینامیک با endpointهای گران‌قیمت — جست‌وجو، ورود، تسویه‌حساب، یک APIای که به پایگاه‌داده دست می‌زند — می‌تواند با چند هزار درخواستِ کاملاً معتبر در ثانیه از پا دربیاید که هیچ فیلترِ بسته‌ای هرگز به آن اعتراض نمی‌کند، و این دقیقاً همان چیزی است که یک سپرِ لایهٔ اپلیکیشن برایش ساخته شده. اما اولین حرکتِ ارزان، آن افزودنی نیست: میکروکش و rate limitهای به‌ازای هر مسیر در مرحله‌های سه و چهار است، که بیشترِ برتریِ ریاضیِ آن را رایگان از بین می‌برد.

آن را به عمل درآورید

VPS ظرف 15 min آنلاین، اختصاصی ظرف 2–12 h تحویل داده می‌شود. از $30.00 با کریپتو شارژ کنید — بدون هویت پیوست‌شده.

استقرار یک VPS