أولًا، تأكّد أنه هجوم فعلاً
أكثر الأخطاء كلفة أثناء انقطاع هو التعامل مع الشيء الخطأ. عبارة "الموقع بطيء" عرَض تشترك فيه حالات مختلفة تمامًا: فيضان حقيقي، أو نشر جديد (deploy) تضمّن استعلامًا بلا فهرسة، أو مهمة cron بدأت بتفريغ قاعدة البيانات في الدقيقة نفسها من كل ساعة، أو زاحف اكتشف بحثك ذا السمات (faceted search)، أو رابط وصل إلى الصفحة الرئيسية لموقع كبير، أو قرص امتلأ. كل واحدة من هذه الحالات تبدو متطابقة من متصفح، والاستجابات المناسبة لها متنافية: فأنت لا تريد تحديد معدل عملائك الحقيقيين بسبب فهرس نسيه أحد أثناء ترحيل (migration) لقاعدة البيانات.
ثلاثة أسئلة تفصل بينها في أقل من دقيقة. هل حجم حركة المرور غير طبيعي فعلاً، أم أن الحركة طبيعية والخادم هو البطيء؟ يظهر الفيضان كتغيّر مفاجئ في الحزم أو الطلبات في الثانية؛ بينما يظهر النشر السيئ كحجم طلبات طبيعي مع زمن استجابة منهار. هل تغيّر شيء من جانبك خلال الساعة الأخيرة؟ تحقّق من سجل النشر قبل جدار الحماية — فالأعطال ذاتية السبب تفوق الهجمات بفارق كبير على البنية التحتية الصغيرة. هل الحمل موزّع على الموقع كله، أم مركّز على مسار واحد؟ الفيضانات الحقيقية عادة عشوائية أو موجَّهة إلى الصفحة الرئيسية؛ أما نقطة نهاية مكلفة يقصفها مئة عميل فهي أقرب إلى إساءة استخدام منها إلى DDoS، ولها إصلاح أرخص بكثير.
أجب عن هذه الأسئلة ثم تابع. بقية هذا الدليل تفترض أن الإجابة كانت: الحجم غير طبيعي، ولم يتغيّر شيء من جانبك، والجهاز يغرق.
SP·02الفرز خلال خمس دقائق
لا يوجد سوى نمطي عطل مهمّين، ويحتاج كل منهما استجابة معاكسة للآخر. فإما أن الأنبوب ممتلئ — تصل الحزم أسرع مما تستوعبه وصلة الصعود أو تستطيع النواة معالجته، ويخسر خادمك حركة مرور قبل أن يراها أي من برامجك — أو أن الأنبوب سليم والتطبيق مُنهَك، لأن طلبات سليمة الصياغة تصل أسرع مما يستطيع الإجابة عنها. الخلط بين الحالتين يهدر الساعة: فضبط nginx لا يفيد شيئًا أمام رابط مُشبَع، وشراء نطاق ترددي إضافي لا يفيد شيئًا أمام فيضان طلبات.
فرّق بينهما بمقارنة رقمين. انظر إلى عدادات الواجهة وتوزيع المعالج (CPU) في الوقت نفسه. إن كانت بايتات rx ثابتة قرب سرعة منفذك، وإن كانت عدادات dropped أو overrun في ارتفاع، وإن كان الوقت يذهب إلى المقاطعات البرمجية (soft interrupts) لا إلى تطبيقك، فالفيضان في الطبقة 3 أو 4 وهو مشكلة سعة. أما إن كان النطاق الترددي عاديًا لكن مجمع العمّال (worker pool) مُشبَعًا، والاتصالات تصطف في طابور، وسجل الوصول مليء بطلبات تبدو معقولة، فالمشكلة في الطبقة 7 وهي مشكلة تصفية.
ثم صنِّف حالة الطبقة 3/4 خطوة أخرى، لأن الأنواع الفرعية تتصرف بشكل مختلف. فيضان SYN يظهر كعشرات الآلاف من المقابس نصف المفتوحة في حالة SYN-RECV؛ وتتعامل النواة مع هذا جيدًا متى كانت syncookies مفعّلة. فيضان UDP أو تضخيم (amplification) يظهر كحجم وارد ضخم على منافذ لا تستمع عليها أصلاً — انعكاسات DNS وNTP وmemcached وCLDAP — ولا شيء تشغّله يمكن أن يساعد، لأن الضرر يقع فعليًا بحلول وصول الحزم إلى بطاقة الشبكة (NIC) لديك. فيضان تجزئة أو حزم خام يظهر كعدد مرتفع من الحزم في الثانية مع نطاق ترددي متواضع، وهو ما يُنهك المعالج بدلاً من الرابط. دوِّن أيّ نوع لديك قبل أن تلمس ملف إعداد.
ما لا يمكنك إصلاحه من داخل الجهاز
هذا هو الجزء الذي تتجاهله معظم المقالات، وهو الجزء الذي يقرر ما إذا كانت ساعتك مثمرة أم لا. قاعدة جدار حماية على الهدف لا تُنقذ وصلة صعود مُشبَعة. أمر iptables DROP الخاص بك يعمل على الجهاز الواقع في نهاية الأنبوب — والحزمة تكون قد عبرت بالفعل رابط الوصل (transit)، واستهلكت بالفعل النطاق الترددي الذي تدفع ثمنه، وأزاحت بالفعل حزمة مستخدم حقيقي. إسقاطها محليًا يحمي تطبيقك من هدر دورات معالجة، وهذا يستحق شيئًا، لكنه لا يحمي نطاقك الترددي بشيء على الإطلاق.
السقف الصادق لخادم واحد هو تقريبًا الأصغر بين رقمين: سرعة المنفذ الذي يتصل عبره، وعدد الحزم في الثانية التي يستطيع معالجه تصنيفها. منفذ بسعة 1 Gbps يمتلئ عند 1 Gbps بصرف النظر عن مدى أناقة مجموعة قواعدك، وفيضان متواضع من حزم صغيرة قد يستنزف نواتين أو ثلاثًا في معالجة المقاطعات قبل أن يبدو رقم النطاق الترددي مقلقًا بوقت طويل. بعد تلك النقطة، الشيء الوحيد الذي يساعد هو جهاز أبعد باتجاه المنبع تفوق سعته سعة الهجوم ويُسقط حركة المرور قبل أن تصل إلى رابطك أصلاً. هذه هي التنقية، وهذا هو سبب بقاء كل جهاز في أسطولنا خلف 1.5 Tbps من التخفيف الدائم بدلاً من جدار حماية أكبر.
والنتيجة الملازمة لا تقل أهمية: إن لم تكن لديك حماية من جهة المنبع، فإن استجابة مزوّدك لهجوم حجمي (volumetric) كبير هي إسقاط التوجيه (null-route) عن عنوانك، لأن البديل هو إضعاف كل عميل آخر على ذلك الرابط. هذا ليس سوء نية، بل حساب رياضي بحت — ويعني أن المهاجم يفوز بجعلك مكلفًا لا بكسر أي شيء. معرفة مسبقًا ما إذا كان التخفيف مشمولاً في خطتك أو أنه إضافة مدفوعة لم تُفعِّلها قط هو تحقّق يستغرق خمس دقائق يستحق القيام به اليوم لا أثناء الحادثة. لدينا هو مشمول في كل خطة، وهو الترتيب الوحيد الذي يفيد في الثالثة صباحًا.
SP·04الطبقة 7: الفيضان الذي يشبه حركة المرور الحقيقية
فيضان طبقة التطبيق أصعب لأن كل طلب منفرد فيه مشروع. فيضان HTTP جيد البناء يُتمّم مصافحة TCP، ويتفاوض على TLS، ويرسل طلب GET / صالحًا بوكيل مستخدم (User-Agent) يبدو معقولاً، ويقرأ الاستجابة. لا توجد فيه حزمة واحدة مشوَّهة. ما يقتلك هو الحساب الرياضي: الطلب لا يكلّف المهاجم تقريبًا شيئًا لإرساله، بينما يكلّفك استعلام قاعدة بيانات، وتصيير قالب (template render)، ومئة ميلي ثانية من عامل لم يعد يخدم أحدًا غيره الآن.
العلامات الدالة موجودة في سجل الوصول الخاص بك، وعادة ما تكون واضحة بمجرد أن تبحث عنها بدلاً من أن تنظر إليها فحسب. سلاسل استعلام لتعطيل التخزين المؤقت (cache-busting) — آلاف الطلبات إلى /?1234567، كل واحد منها رابط فريد يُهزم كل تخزين مؤقت تملكه — هي التوقيع الأكثر شيوعًا على الإطلاق. توزيع لوكيل المستخدم بلا ذيل طويل: حركة المرور الحقيقية مزيج فوضوي من مئات إصدارات المتصفحات، بينما الفيضان غالبًا ثلاث سلاسل متكررة مليون مرة، أو سلسلة واحدة لا يستخدمها أي شخص حقيقي. حقل إحالة (referrer) متطابق في كل مكان. طلبات تتجاوز أصولك الثابتة كليًا — المتصفح الحقيقي يجلب CSS والخطوط والصور بعد HTML؛ أما عميل الفيضان فيطلب HTML ثم يرحل. وتوزيع مصادر مسطّح أكثر مما ينبغي: شبكة روبوتات (botnet) موزّعة على عشرة آلاف عنوان سكني ترسل كل منها طلبين في الثانية تبدو كشعبية إلى أن تلاحظ أن معدل كل عنوان منتظم بشكل مثير للريبة.
ثم هناك النمط الذي لا يحتاج تقريبًا إلى أي حركة مرور: الهجوم البطيء. بضع مئات من الاتصالات تُفتح، وترسل ترويسة واحدة كل عشرين ثانية ولا تنتهي أبدًا، ستشغل كل عامل لديك بينما يبقى رسم النطاق الترددي مسطّحًا. الإصلاح ليس تحديد معدل — فمعدل الطلبات ضئيل جدًا — بل مهلات صارمة للترويسة والجسم، وهذا هو سبب ظهورها في كتلة الإعداد الأولى أدناه بدلاً من أن تكون فكرة لاحقة.
SP·05أربعة ضوابط، مرتّبة حسب الأثر لكل دقيقة
تحت الضغط، ابدأ بالأمر الأعلى تأثيرًا. الترتيب أدناه ليس عشوائيًا؛ بل هو تنازلي تقريبًا بحسب مقدار الحمل الذي يخفّفه كل ضابط لكل دقيقة من انتباهك.
- قدِّم شيئًا رخيصًا. تخزين مؤقت مصغّر (micro-cache) مدته ثلاثون ثانية أمام تطبيقك يحوّل ألف طلب متطابق في الثانية إلى إصابة واحدة للمصدر (origin hit) و999 قراءة من الذاكرة. إنه أكبر رافعة منفردة أمام كل فيضان HTTP تقريبًا، ولا يكلّف سوى كتلة توجيه واحدة، وهو غالبًا آمن دائمًا بالنسبة لحركة المرور المجهولة. أضف
proxy_cache_lockحتى لا يرسل إخفاق التخزين المؤقت (cache miss) قطيعًا هائجًا (thundering herd) إلى الواجهة الخلفية. - احدّ من التزامن وقصِّر المهلات. استخدام
limit_connلكل عنوان مع مهلات صارمة للترويسة والجسم و keepalive يقضي على الهجمات البطيئة تمامًا ويمنع أي عميل منفرد من احتلال مجمع العمّال لديك. هذا هو الضابط الأقل كلفة على المستخدمين الحقيقيين. - حدِّد المعدل، مع اندفاع (burst). استخدام
limit_reqباندفاع معقول دقيق لكنه أبطأ في الضبط، وهو الضابط الذي يولّد نتائج إيجابية كاذبة إن ضبطته من الذعر بدلاً من خط الأساس الخاص بك. تحتاج إلى معرفة معدل طلباتك الطبيعي في الثانية لكل عميل قبل أن تختار رقمًا — وهذا هو سبب أهمية التشريح اللاحق (post-mortem) في نهاية هذا الدليل أكثر مما يبدو. - احظر، بدقة وعلى مضض. إسقاط شبكات محددة يفلح حين تكون المصادر مركّزة ولا يفيد بشيء حين لا تكون كذلك. كما أنه يشيخ بشكل سيئ: فكل حظر تضيفه أثناء حادثة قد يكون عميلاً ترفضه بصمت بعد ثلاثة أشهر. استخدم مجموعة (set) بمهلة انتهاء كي تنتهي صلاحية القواعد من تلقاء نفسها.
لاحظ ما ليس مدرجًا في القائمة: حظر عناوين IP منفردة يدويًا، وإعادة تشغيل خادم الويب مرارًا، وتعطيل جدار الحماية "لترى إن كان ذلك يساعد". الأول أبطأ من أن يُحدث فرقًا أمام مصدر موزَّع، والثاني يهدر كل اتصال دافئ كان لديك، والثالث هو بالضبط كيف تتحوّل حادثة إلى اختراق.
SP·06لا تخض المعركة من الجهاز الذي يحمل بياناتك
كل ضابط مما سبق تزداد قيمته حين يعمل في مكان آخر غير الجهاز الذي يحمل قاعدة بياناتك. إن كانت حافتك (edge) عقدة منفصلة، فإن الفيضان ينتهي عند جهاز مهمته الوحيدة هي إنهاء الفيضانات: فهو يخزّن مؤقتًا، ويحدّد المعدل، ويُسقط الحزم وعنوان العميل الحقيقي في يده، ولا يرى المصدر أبدًا سوى الباقي الصغير المُصفّى عبر نفق خاص. حين تسقط الحافة، تستبدلها خلال 15 min ولا تخسر شيئًا، لأنه لا يوجد عليها شيء أصلاً. أما حين يسقط المصدر فلديك انقطاع واستعادة.
هذا الفصل يُغلق أيضًا الثغرة الالتفافية التي تجعل معظم التخفيف مجرد ديكور. إن كان المصدر لا يزال يملك مستمعًا عامًا، فإن مهاجمًا يعثر على عنوانه — عبر DNS السلبي، أو قيد في سجلات شفافية الشهادات، أو سجل MX، أو معاينة رابط — يستطيع التصويب متجاوزًا كل ضابط أعددته وضرب التطبيق مباشرة. هذا شائع بما يكفي ليستحق أن يُعامل كالحالة الافتراضية لأي موقع "محمي" إلى أن يثبت العكس. بناء النسخة التي تصمد موضوع دليل قائم بذاته: وكيل عكسي خارجي بلا أي مستمع عام على المصدر إطلاقًا.
تحذير واحد بخصوص منتصف الحادثة: هذه بنية معمارية، لا إسعافًا أوليًا. إقامة حافة، ونقل DNS، وإعادة بناء نفق أثناء التعرض لهجوم هي مهمة مدتها ساعتان تُنجَز بشكل سيئ تحت الضغط، وتغيير DNS وحده لن يسري مفعوله طوال المدة التي يحددها TTL الخاص بك. إن كانت لديك، استخدمها. وإن لم تكن، اجتز الساعة بالضوابط المتوفرة لديك، وابنِها في الأسبوع الهادئ التالي — وهو بالضبط الوقت الذي لا يفعل فيه أحد ذلك.
SP·07اليوم الذي تكون فيه أنت العاكس، لا الهدف
هناك نسخة ثانية من هذه الحادثة لا يكون فيها خادمك هو الضحية ولا يُخبرك بها أحد. مُحلِّل (resolver) مفتوح، أو خدمة NTP مكشوفة، أو memcached بلا مصادقة على واجهة عامة، أو مستجيب SSDP أو CLDAP داخل حاوية — كل واحدة من هذه تجيب على طلب صغير منتحَل الهوية برد أكبر بكثير، موجَّه إلى شخص آخر. من جانبك، تكون الأعراض معكوسة: النطاق الترددي الصادر مرتفع، والوارد متواضع، وتطبيقك بخير، وأول إشارة حقيقية هي إشعار إساءة استخدام أو منفذ مُعلَّق.
هذا الفحص يستغرق دقيقة وينتمي إلى دليل التشغيل نفسه، لأنه الأمر نفسه الذي شغّلته بالفعل أثناء الفرز. يجب ألا يُظهر ss -tulpn شيئًا مرتبطًا بعنوان عام لم تضعه أنت عمدًا هناك، وتستحق خدمات UDP شكًّا خاصًا لأنها هي التي تُضخِّم. يجب أن يكون أي مُحلِّل تكراري (recursive resolver) مرتبطًا بـ localhost أو بعنوان نفق؛ ويجب ألا يكون memcached وRedis قابلين للوصول من الإنترنت على الإطلاق؛ وأي حاوية تنشر منفذًا بـ -p 0.0.0.0: تكون قد فتحت للتو ثغرة عبر جدار الحماية الذي أعددته، لأن Docker يكتب قواعده الخاصة قبل قواعدك. تلك الأخيرة تفاجئ الناس في كل مرة.
الشكل نفسه يغطي الفيضانات الصادرة من جهاز سبق اختراقه بالفعل، وهو السبب الآخر الذي يجعل مزوّدًا يُسقط فجأة التوجيه عن عنوان. إن كان رسمك البياني للحركة الصادرة مرتفعًا وتطبيقك خاملاً، فتوقف عن قراءة الإعدادات وابدأ بفحص العمليات (processes) — فذلك اختراق، لا مشكلة سعة، والاستجابة الصحيحة هي إعادة بناء الجهاز من نسخة احتياطية معروفة السلامة بدلاً من تصفيته.
SP·08بعد أن يتوقف: التشريح اللاحق والعدّة الدائمة
الهجمات تتوقف. عادة يملّ المهاجم، وأحيانًا يجعله التخفيف عديم الجدوى، وفي بعض الأحيان كان مجرد اشتراك في خدمة إغراق (booter) محدود المدة انتهى ببساطة. الإغراء في تلك اللحظة هو ترك كل شيء كما هو والذهاب للنوم، وهذا بالضبط كيف يتحوّل تحديد معدل مؤقت إلى رمز 429 دائم ومنسي وصامت يطال بلدًا بأكمله بعد تسعة أشهر. اقضِ عشرين دقيقة في إغلاق الحلقة بينما الأمر لا يزال طازجًا.
ثلاث نتاجات تستحق الإعداد. خط أساس: معدل طلباتك الطبيعي في الثانية، ومعدلك الطبيعي لكل عميل، ونطاقك الترددي الطبيعي عند الذروة. من دون هذه الأرقام، كل حد تضبطه أثناء الحادثة التالية مجرد تخمين، ونصفها سيكون خاطئًا في الاتجاه الذي يؤذي العملاء. قائمة تراجع: كل ما غيّرته، مع تاريخ وسبب، حتى لا يتحوّل إعداد الطوارئ بصمت إلى الإعداد الدائم. دليل تشغيل طوله أربعة أوامر ومحفوظ في مكان تستطيع الوصول إليه حين يكون الموقع متوقفًا — لا على الخادم نفسه، ولا في رأسك وحده.
ثم أغلق الفجوتين البنيويتين، لأن هذا هو موضع هذه الطبقة في المكدّس. التخفيف هو ما يمتص الحجم، والحافة هي ما يُبقي الفيضان بعيدًا عن بياناتك، ولا يزال يتعيّن على الجهاز خلفهما أن يكون مُحصَّنًا بشكل صحيح وأن تكون له نسخ احتياطية خارج الموقع قابلة للاستعادة، لأن الحادثة التي تلي هذه قد لا تكون فيضانًا على الإطلاق. الخادم الذي ينجو من DDoS ثم يفقد قرصه بعد شهر لم يكن صامدًا يومًا — بل كان محظوظًا مرتين.
SP·09خطوة بخطوة
-
01
تأكّد قبل أن تغيّر أي شيء
احصل على صورة صادقة واحدة للجهاز قبل أن تلمس ملف إعداد. أنت تبحث عن تغيّر مفاجئ في الحزم أو الطلبات، وعن الجهة التي يذهب إليها الوقت — فتطبيق محروم من المعالج بسبب المقاطعات البرمجية حادثة مختلفة تمامًا عن تطبيق ينتظر قاعدة بيانات.
# 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 واحد، وتبدو متطابقة من الخارج.
-
02
قِس شكل حركة المرور خلال ستين ثانية
الآن صنِّفها. ثلاثة أسئلة: كم عدد المصادر المتمايزة، وما البروتوكول والحالة، وإن كانت HTTP — ما المسارات وما الوكلاء (agents). تحدد الإجابات الضابط الذي تلجأ إليه، وتستغرق نحو دقيقة لجمعها.
# 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اقرأ النتيجة في ضوء العلامات الدالة: سلاسل استعلام فريدة على مسار واحد، وقائمة وكلاء مستخدم بلا ذيل طويل، وغياب طلبات لأصولك الثابتة، أو معدل لكل عنوان منتظم بشكل غريب. إن كان النطاق الترددي مرتفعًا على منافذ لا تستمع عليها، توقف هنا — فذلك فيضان حجمي والخطوة السادسة هي الخطوة الوحيدة المهمة.
-
03
قدِّم شيئًا رخيصًا، وحدَّ من الاتصالات
الأولوية القصوى أولاً. تخزين مؤقت مصغّر مدته ثلاثون ثانية يُذيب فيضان طلبات مجهولة متطابقة في إصابة واحدة للمصدر، والمهلات الصارمة تقضي على الهجمات البطيئة التي لا يستطيع تحديد المعدل رصدها. ضع كليهما في كتلة
http، ثم أعد التحميل بدلاً من إعادة التشغيل كي تحافظ على اتصالاتك الدافئة.# /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
-
04
حدِّد معدل العميل الحقيقي، لا وكيلك أنت
إن كان أي شيء يقف أمام nginx، فإن كل طلب يصل من عنوان واحد، وحد لكل عنوان سيَخنُق الوكيل بدلاً من المهاجم — أو يحظره تمامًا ويُسقط الموقع بينما أنت تدافع عنه. ثِق بترويسة إعادة التوجيه (forwarded header) من عنوان الوكيل فقط، لا من الإنترنت مطلقًا، ثم طبّق الحد.
# /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. إن كانت العناوين التي يجري تحديد معدلها تشبه عملاءك، فالمعدل منخفض جدًا — ارفعه. الحد الذي يحظر مستخدمين حقيقيين هو انقطاع تسببت به بنفسك. -
05
احظر بدقة، وامنح كل حظر أجلاً لانتهاء الصلاحية
لا يستحق القيام به إلا حين تُظهر الخطوة الثانية مصادر مركّزة. استخدم مجموعة (set)، لا ألف قاعدة — فالبحث في
ipsetيستغرق زمنًا ثابتًا، بينما تُفحص سلسلةiptablesالطويلة عند كل حزمة وتتحوّل هي نفسها إلى حرمان من الخدمة. امنح كل قيد مهلة انتهاء كي لا يتحوّل طارئ اليوم إلى قائمة حظر صامتة العام المقبل.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
قاوم إغراء حجب بلد بأكمله جغرافيًا ما لم تستطع تسمية العملاء الذين تقطع عنهم الخدمة. ولا تستخدم أبدًا
ufw disableلاختبار نظرية: فجهاز بلا جدار حماية تحت هجوم فعلي هو بالضبط كيف تتحوّل حادثة نطاق ترددي إلى اختراق. -
06
صعِّد إلى الطبقة القادرة فعلاً على الاستيعاب
إن كانت عدادات الواجهة تقول إن الرابط مُشبَع، أو كانت الحزم المُسقَطة (drops) في ارتفاع بينما معالجك خامل، فقد بلغت سقف كل ما يمكنك فعله على الخادم. تأكّد من هذه القراءة، ثم صعِّد بدلاً من الاستمرار في الضبط.
# 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 حسن البناء لا هجومًا حجميًا، فتلك هي الحالة التي تحتاج فيها إلى درع طبقة 7 (L7)، لأن فيضانات الطلبات لا تُميَّز عن المستخدمين على مستوى الحزمة ويجب الحكم عليها في مستوى أعلى. إن لم يكن لديك أي تخفيف على الإطلاق، فخياراتك الواقعية هي الانتقال خلف حافة تملك ذلك، أو الانتظار — والتخطيط للأول قبل الهجوم التالي.
-
07
أغلق الحلقة: تحقّق، وتراجع، ثم دوِّن ذلك
تحقّق من خارج الجهاز، لا من واجهة أوامر (shell) عليه. ثم تراجع عن إجراءات الطوارئ عمدًا، واحتفظ بتلك التي كانت دائمًا فكرة جيدة، وسجّل الأرقام كي تبدأ الحادثة التالية من خط أساس بدلاً من تخمين.
# 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احتفظ بالتخزين المؤقت، والمهلات، وsyncookies — فتلك تحسينات دائمة. تراجع عن حدود المعدل الصارمة إلى خط الأساس الذي قسته زائد هامش، ودع قيود ipset تنتهي صلاحيتها من تلقاء نفسها. ثم دوِّن الأوامر الأربعة من الخطوتين الأولى والثانية في دليل تشغيل محفوظ في مكان ليس هذا الخادم، إلى جانب معدل طلباتك الطبيعي في الثانية ونطاقك الترددي الطبيعي عند الذروة. الهجوم التالي يصبح حادثة أقصر لمجرد أن هذين الرقمين موجودان.


