جميع الأنظمة تعمل بكفاءة 6 منطقة خارجية دفع دون KYC
Hands-on الدليل الميداني

هجوم DDoS على VPS الخاص بك: دليل تشغيل الساعة الأولى

الدقائق العشر الأولى تقرر شكل الساعات الثلاث التالية، ومعظم الناس يقضونها في التخمين — إعادة تشغيل الخدمات، وحظر عناوين عشوائيًا، وقراءة لوحة معلومات لا تقول أكثر من أن الموقع متوقف. هذا هو التسلسل الذي نطبّقه فعليًا على الأسطول: تأكّد من أن الهجوم حقيقي، وقِس شكله، وخفّف الحمل الذي يمكن تخفيفه من داخل الجهاز، وتعرّف على النقطة الدقيقة التي لم يعد عندها ما تكتبه على الخادم مهمًّا على الإطلاق ولم يعد يهم عندها شيء سوى 1.5 Tbps من تنقية المنبع (scrubbing) الماثلة أمامه. مكتوب من أجل VPS واحد بسعر ابتداءً من $8.00/شهريًا، لا من أجل مركز عمليات شبكة (NOC).

حُدِّث في 2026-09-06 · قراءة 15 دقيقة · عمليات الأسطول
في هذه الصفحة
  1. أولًا، تأكّد أنه هجوم فعلاً
  2. الفرز خلال خمس دقائق
  3. ما لا يمكنك إصلاحه من داخل الجهاز
  4. الطبقة 7: الفيضان الذي يشبه حركة المرور الحقيقية
  5. أربعة ضوابط، مرتّبة حسب الأثر لكل دقيقة
  6. لا تخض المعركة من الجهاز الذي يحمل بياناتك
  7. اليوم الذي تكون فيه أنت العاكس، لا الهدف
  8. بعد أن يتوقف: التشريح اللاحق والعدّة الدائمة
  9. خطوة بخطوة
SP·01

أولًا، تأكّد أنه هجوم فعلاً

أكثر الأخطاء كلفة أثناء انقطاع هو التعامل مع الشيء الخطأ. عبارة "الموقع بطيء" عرَض تشترك فيه حالات مختلفة تمامًا: فيضان حقيقي، أو نشر جديد (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) لديك. فيضان تجزئة أو حزم خام يظهر كعدد مرتفع من الحزم في الثانية مع نطاق ترددي متواضع، وهو ما يُنهك المعالج بدلاً من الرابط. دوِّن أيّ نوع لديك قبل أن تلمس ملف إعداد.

SP·03

ما لا يمكنك إصلاحه من داخل الجهاز

هذا هو الجزء الذي تتجاهله معظم المقالات، وهو الجزء الذي يقرر ما إذا كانت ساعتك مثمرة أم لا. قاعدة جدار حماية على الهدف لا تُنقذ وصلة صعود مُشبَعة. أمر 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

خطوة بخطوة

  1. 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 واحد، وتبدو متطابقة من الخارج.

  2. 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

    اقرأ النتيجة في ضوء العلامات الدالة: سلاسل استعلام فريدة على مسار واحد، وقائمة وكلاء مستخدم بلا ذيل طويل، وغياب طلبات لأصولك الثابتة، أو معدل لكل عنوان منتظم بشكل غريب. إن كان النطاق الترددي مرتفعًا على منافذ لا تستمع عليها، توقف هنا — فذلك فيضان حجمي والخطوة السادسة هي الخطوة الوحيدة المهمة.

  3. 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
  4. 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. إن كانت العناوين التي يجري تحديد معدلها تشبه عملاءك، فالمعدل منخفض جدًا — ارفعه. الحد الذي يحظر مستخدمين حقيقيين هو انقطاع تسببت به بنفسك.

  5. 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 لاختبار نظرية: فجهاز بلا جدار حماية تحت هجوم فعلي هو بالضبط كيف تتحوّل حادثة نطاق ترددي إلى اختراق.

  6. 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)، لأن فيضانات الطلبات لا تُميَّز عن المستخدمين على مستوى الحزمة ويجب الحكم عليها في مستوى أعلى. إن لم يكن لديك أي تخفيف على الإطلاق، فخياراتك الواقعية هي الانتقال خلف حافة تملك ذلك، أو الانتظار — والتخطيط للأول قبل الهجوم التالي.

  7. 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 تنتهي صلاحيتها من تلقاء نفسها. ثم دوِّن الأوامر الأربعة من الخطوتين الأولى والثانية في دليل تشغيل محفوظ في مكان ليس هذا الخادم، إلى جانب معدل طلباتك الطبيعي في الثانية ونطاقك الترددي الطبيعي عند الذروة. الهجوم التالي يصبح حادثة أقصر لمجرد أن هذين الرقمين موجودان.

SP·10 — الأسئلة الشائعة

إجابات سريعة

كيف أميّز هجوم DDoS حقيقيًا عن ارتفاع طبيعي في حركة المرور؟

انظر إلى الشكل لا إلى الحجم. الارتفاعات العضوية لها بنية: تأتي من شبكات كثيرة، ووكلاء المستخدم فيها ذيل طويل فوضوي، ويجلب العملاء CSS والصور بعد HTML، والمُحيل عادة يشير إلى مكان حقيقي. أما الفيضان فمسطّح ومتكرر — الحفنة نفسها من الوكلاء، ولا طلبات لأصول ثابتة، ومعدلات موحدة لكل عميل، وغالبًا سلاسل استعلام فريدة مصمَّمة لهزيمة التخزين المؤقت. هناك أمران أيضًا يستبعدان نفسيهما بسرعة: تحقّق من سجل النشر وجدول cron قبل أن تلوم أي أحد، لأن استعلامًا بطيئًا نُشر قبل ساعة ينتج الأعراض نفسها ويحتاج الإصلاح المعاكس.

هل يستطيع iptables أو fail2ban إيقاف هجوم DDoS؟

يساعدان أمام الهجمات الصغيرة والمركّزة، ولا يفيدان بشيء أمام الهجمات الحجمية. كلاهما يعمل على الجهاز الواقع في نهاية الأنبوب، لذا بحلول وقت تفعيل القاعدة تكون الحزمة قد استهلكت نطاقك الترددي بالفعل — أنت توفّر دورات معالج، لا سعة رابط. كما أن fail2ban رد فعل بطبيعة تصميمه: فهو يقرأ سجلاً بعد وقوع الحدث، وهو ما يجعله متأخرًا بدقائق عن الفيضان، ويمكنه حظر وكيلك العكسي الخاص إن لم تكن قد أعددت عنوان العميل الحقيقي. استخدمهما كإجراء نظافة أمام القوة الغاشمة والماسحات؛ واستخدم تنقية المنبع أمام الحجم.

هل سيساعد تغيير عنوان IP الخاص بخادمي؟

لفترة قصيرة ونادرًا، ويكلّفك كل ذاكرة تخزين مؤقت لـDNS في العالم. إن كان المهاجم يستهدف اسم مضيف، فإن العنوان الجديد يظهر في DNS خلال دقائق ويتبعه الفيضان؛ ولا تُجدي الخطوة نفعًا إلا حين يكون الهدف هو العنوان الخام نفسه ولا تملك وسيلة سهلة لإعادة تحليله (re-resolve). النسخة التي تنجح فعلاً بنيوية لا تجميلية: ضع حافة قابلة للاستبدال في المقدمة، وأبقِ المصدر غير قابل للوصول من الإنترنت العام، عندها يصبح العنوان الواقع تحت الهجوم عنوانًا تستطيع التخلص منه خلال 15 min دون المساس ببياناتك.

هل يضيف التخفيف من DDoS زمن استجابة أو يُعطّل أي شيء؟

تنقية الطبقة 3/4 تعمل دائمًا وغير مرئية عمليًا — فالحزم تمر عبر مسار تصفية بدلاً من أن تُحوَّل فقط أثناء حدث ما، لذا لا توجد لحظة تحوّل احتياطي (failover) ولا عقوبة تُذكر على حركة المرور الطبيعية. أما تصفية طبقة التطبيق فمختلفة، لأن الحكم على ما إذا كان الطلب بشريًا يعني أحيانًا تحدّيه، وأي تحدٍّ يؤثر في عدد صغير من العملاء غير المعتادين. نمطا العطل اللذان يستحقان التخطيط لهما هما الاستجابات المخزَّنة مؤقتًا التي تُقدَّم لمستخدمين مسجَّلين الدخول، وهي مشكلة مفتاح تخزين مؤقت (cache-key) لا مشكلة تخفيف، وعملاء واجهة برمجية (API) لا يستطيعون الإجابة عن تحدٍّ، وهذا هو سبب استثنائك لمسارات API لديك عمدًا بدلاً من اكتشاف ذلك لاحقًا.

هل يمكن أن يكون خادمي هو من يهاجم شخصًا آخر؟

يحدث هذا أكثر مما يتوقع الناس، والأعراض معكوسة: نطاق ترددي صادر مرتفع، ووارد طبيعي، وتطبيق يبدو بخير، وإشعار إساءة استخدام كأول إشارة حقيقية. الأسباب المعتادة هي مُحلِّل DNS مفتوح، أو خدمة NTP أو memcached مكشوفة، أو حاوية نشرت منفذًا على 0.0.0.0 وكتبت قاعدة جدار الحماية الخاصة بها قبل قاعدتك. شغّل ss -tulpn وتأكّد أنه لا شيء لم تقصده مرتبط بعنوان عام. إن كان الصادر مرتفعًا ولا توجد خدمة مشروعة تفسّر ذلك، عامله كاختراق وأعد البناء من نسخة احتياطية معروفة السلامة بدلاً من محاولة التصفية للخروج من المأزق.

الحماية على الطبقة 3/4 مشمولة — هل ما زلت بحاجة إلى درع طبقة 7؟

الأمر يعتمد كليًا على ما تُشغّله. موقع ثابت، أو خادم ألعاب، أو أي شيء يكون فيه الحمل حزمًا لا تصييرات صفحات (page renders) تغطيه تنقية مستوى الشبكة وحدها جيدًا. أما تطبيق ديناميكي بنقاط نهاية مكلفة — بحث، وتسجيل دخول، ودفع، وواجهة برمجية تلامس قاعدة البيانات — فيمكن أن يُسقطه بضعة آلاف من الطلبات الصحيحة تمامًا في الثانية التي لن يعترض عليها أي مرشِّح حزم أبدًا، وهذا بالضبط ما صُمم من أجله درع طبقة التطبيق. غير أن الخطوة الأولى الرخيصة ليست الإضافة: بل التخزين المؤقت المصغّر وحدود المعدل لكل مسار في الخطوتين الثالثة والرابعة، وهما ما يزيلان معظم الأفضلية الحسابية مجانًا.

طبّقها عمليًا

VPS متصل خلال 15 min، والمخصص يُسلَّم خلال 2–12 h. اشحن بدءًا من $30.00 بالعملات المشفّرة — دون أي هوية مرتبطة.

انشر خادم VPS