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

في ما يلي خمس خطوات تعطي على الخادم نفسه نتيجة فعلية: قطع الماسحات، وتحديد الوتيرة، وعدم قطع ما تحتاج إليه، ثم الحجب الصحيح إن كان لا بد منه. كل ذلك يمكن فحصه وضبطه في جلسة واحدة.

الخطوة 1. انظر من يصنع البقعة فعلًا

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

sudo ls -lS /var/log/apache2/*access*.log /var/log/apache2/*requests*.log 2>/dev/null

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

sudo awk '{print $1}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head -20
sudo grep '^203\.0\.113\.' /var/log/apache2/access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -20

الأمر الثاني يجيب عن السؤال الرئيسي. إن ظهرت في المخرجات عشرات الصفحات المختلفة من موقعك، فهذا جامع محتوى: غير ضار، ويكفي ألا تحسبه زائرًا. وإن ظهر هناك /wp-login.php و/.env و/phpmyadmin وغيرها مما لا تملكه أصلًا، فهذا ماسح، وتتكفل به الخطوة 2. في nginx تكون السجلات في /var/log/nginx/، والحقول نفسها.

الخطوة 2. سجن للطلبات الموجهة إلى ما لا تملكه

مسار الطلب لا يمكن تزويره — فهو النية ذاتها، بخلاف العنوان وUser-Agent. المرشّح /etc/fail2ban/filter.d/web-probes.local:

[Definition]
failregex = ^<HOST> .* "(GET|POST|HEAD) /(wp-login\.php|wp-admin|xmlrpc\.php|\.env|\.git/|phpmyadmin|administrator/)
ignoreregex =

السجن /etc/fail2ban/jail.d/web-probes.local:

[web-probes]
enabled  = true
port     = http,https
filter   = web-probes
logpath  = /var/log/apache2/*access.log
maxretry = 1
findtime = 1h
bantime  = 1d

هنا maxretry = 1 مبرَّر: لا أحد يصل مصادفةً إلى لوحة إدارة غيره. طبّق وتأكد أن المرشّح يلتقط سطورًا في سجلك أنت:

sudo fail2ban-regex /var/log/apache2/access.log /etc/fail2ban/filter.d/web-probes.local
sudo systemctl reload fail2ban && sudo fail2ban-client status web-probes

الخطوة 3. حدّد الوتيرة

يفضح الماسح والزاحف نفسيهما بالسرعة، أيًّا كان البلد الذي يأتيان منه. ثلاثون صفحة في الدقيقة من عنوان واحد عتبة لا تمس إنسانًا يتصفح فهرسًا:

[http-flood]
enabled  = true
port     = http,https
filter   = apache-overflows
logpath  = /var/log/apache2/site_access.log
maxretry = 30
findtime = 60
bantime  = 1h

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

sudo fail2ban-client set http-flood unbanip 203.0.113.10

الخطوة 4. احمِ ما لا يجوز قطعه

قبل تشغيل عمليات الحجب دوّن الاستثناءات — وإلا فإن أول ما يقع تحتها هو ما تحتاج إليه:

[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 203.0.113.0/24 66.249.64.0/19

ما الذي يجب أن يكون في هذا السطر حتمًا ولماذا:

  • عنوانك أنت أو الشبكة الفرعية للمكتب. أكثر أسباب فقدان الوصول إلى الخادم شيوعًا هو قاعدتك أنت، لا هجوم؛
  • النطاق 66.249.64.0/19. يُعرف روبوت بحث Google من User-Agent، لكن الجزء منه الذي ينفّذ JavaScript يأتي بهيئة Chrome/Linux عادي ولا يعرّف عن نفسه. وأي قاعدة من نوع «احجب حركة مراكز البيانات» تصيب هذا الجزء بالذات، ثم تخرج الصفحات من الفهرس بصمت. يمكن فحص عنوان مشبوه باستعلام عكسي: على dig -x 66.249.66.1 +short أن يعيد اسمًا في نطاق googlebot.com؛
  • عناوين إشعارات الدفع. تصل خطافات الويب الخاصة بأنظمة الدفع من خوادم AWS وAzure. فإن أغلقت تلك السحابات بالكامل، تتوقف تأكيدات الدفع عن الوصول، وتعرف ذلك يوم يكتب لك عميل أن المبلغ خُصم ولا وصول لديه. تؤخذ النطاقات من وثائق مزوّد الدفع الذي تتعامل معه.

الخطوة 5. إن كان الحجب ضروريًا رغم كل شيء — احجب شبكة فرعية لا بلدًا

التكاثف الثابت من شبكة واحدة، الذي يدوم أيامًا ويرافقه طلبات متشابهة، هو منفّذ واحد يسير على قائمة. وتُحجب هذه الحالة بدقة ولمدة محددة:

sudo ufw deny from 198.51.100.0/22 to any
sudo ufw status numbered

بالأمر الثاني احفظ رقم القاعدة: رفع الحجب لاحقًا يجري عبره بالذات، sudo ufw delete <الرقم>. اتفق مع نفسك على مدة — شهر مثلًا — وسجّل الرفع في التقويم. فالقواعد التي تُوضع «إلى الأبد» تتحول بعد سنة إلى قائمة لا يجرؤ أحد على لمسها، لأن لا أحد يتذكر لماذا دخلت تلك العناوين إليها.

أما أي شبكة فرعية تحجب، فيمكن معرفتها من العنوان المأخوذ في الخطوة الأولى: whois 198.51.100.17 | grep -iE 'route|cidr|netname'.

ما الذي يتغير بعد هذه الخطوات الخمس

تُقطع الماسحات بحسب النية لا بحسب المنشأ، وتُلتقط من الطلب الأول. ويصطدم الزواحف بحد الوتيرة. ويظل روبوت البحث وإشعارات الدفع يمران كما كانا. أما الحجب الوحيد الذي بقي واسعًا فهو دقيق، بمدة معلومة وسبب مفهوم.

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