ليست كل الهجمات متشابهة. فالهجوم الحجمي يسعى إلى إغراق الوصلة الشبكية: إنه أشبه بازدحام على الطريق السريع، وتتولى حماية By-Hoster التصدي له مسبقا دون أن تحتاج إلى فعل أي شيء.
أما الهجوم التطبيقي، المعروف بهجوم الطبقة 7، فمختلف. إذ يبقى حجم الحركة متواضعا، لكن كل طلب مكلف: عملية بحث، أو صفحة تسجيل دخول، أو سلة مشتريات. بالنسبة إلى الشبكة، هؤلاء زوار عاديون. ولا تتضح طبيعة الحركة إلا على مستوى خادم الويب لديك، ولذلك يجب التصرف هناك.
كيف أعرف ما الذي يضرب خادمي فعلا؟
لا تغير شيئا ما دمت لا تعرف ما يحدث. فالإعداد الموضوع عشوائيا كثيرا ما يحظر زوارك الحقيقيين دون أن يزعج المهاجم.
عناوين IP الأكثر نشاطا:
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
عناوين URL الأكثر طلبا:
awk -F'"' '{print $2}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
الحركة خلال الدقيقة الأخيرة، لتقدير الشدة:
tail -n 100000 /var/log/nginx/access.log | awk '{print $4}' | uniq -c | tail -20
أنت تبحث عن نمط: حفنة من العناوين كثيرة الطلبات، أو عنوان URL واحد يتعرض للقصف، أو وكيل مستخدم متطابق في كل مكان. هذا النمط هو الذي يحدد القاعدة، وليس العكس.
تحقق أيضا مما يتشبع فعلا:
uptime
systemctl status php8.3-fpm --no-pager
في كثير من الأحيان، ليس Nginx هو الذي ينهار، بل PHP أو قاعدة البيانات خلفه.
كيف أحد من معدل الطلبات لكل عنوان في Nginx؟
يستطيع Nginx عد الطلبات لكل عنوان وإبطاء ما يتجاوز الحد. يتم تعريف المناطق في الكتلة http (الملف /etc/nginx/nginx.conf):
http {
limit_req_zone $binary_remote_addr zone=req_par_ip:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=conn_par_ip:10m;
}
ثم التطبيق في كتلة الموقع:
server {
limit_req zone=req_par_ip burst=20 nodelay;
limit_conn conn_par_ip 20;
}
ما يعنيه ذلك عمليا:
| الإعداد | التأثير |
|---|---|
rate=10r/s |
10 طلبات في الثانية لكل عنوان في الوضع العادي |
burst=20 |
يسمح بذروة قدرها 20 طلبا فوق المعدل |
nodelay |
يخدم الذروة فورا بدلا من توزيعها |
limit_conn 20 |
20 اتصالا متزامنا كحد أقصى لكل عنوان |
تحفظ منطقة بحجم 10m نحو 160 000 عنوان، وهذا كاف تماما.
يضع burst دون nodelay الطلبات في قائمة انتظار: فيصبح الموقع بطيئا بدلا من الرفض. ومع nodelay، يمر الزائر العادي الذي يحمل صفحة وموارده العشرين دون إبطاء، بينما يتلقى الروبوت الملح خطأ 503.
احم عناوين URL المكلفة فعلا بصرامة أكبر بدلا من الموقع بأكمله:
location = /wp-login.php {
limit_req zone=req_par_ip burst=3 nodelay;
}
كيف أختبر إعدادات Nginx قبل تطبيقها؟
باستخدام nginx -t، الذي يتحقق من صحة الصياغة دون تطبيق أي شيء، ثم إعادة تحميل فورية بدلا من إعادة التشغيل.
nginx -t
يتحقق هذا الأمر من الصياغة دون مقاطعة أي شيء. يجب أن يعيد syntax is ok ثم test is successful. وبعد ذلك فقط:
systemctl reload nginx
يعيد reload تحميل الإعدادات دون قطع الاتصالات الجارية، على عكس restart. وفي خضم الهجوم، يكون لهذا الفرق أهمية.
راقب ما يتم رفضه:
grep "limiting requests" /var/log/nginx/error.log | tail -20
إذا رأيت عناوينك أو عناوين عملائك تظهر، فالحد منخفض جدا. ارفعه.
كيف أقلل عدد مرات تقديم الصفحة نفسها؟
غالبا ما تكون أفضل طريقة لامتصاص الهجوم هي عدم إعادة حساب الصفحة مع كل طلب. فالصفحة المخزنة مؤقتا لمدة 60 ثانية لا تكلف شيئا تقريبا، مهما كان عدد الطلبات.
http {
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=site:50m inactive=10m;
}
location / {
proxy_cache site;
proxy_cache_valid 200 60s;
proxy_cache_use_stale error timeout updating;
}
السطر proxy_cache_use_stale هو المنقذ: إذا توقفت الواجهة الخلفية عن الاستجابة، يواصل Nginx تقديم آخر نسخة معروفة بدلا من رسالة خطأ.
على WordPress أو PrestaShop أو Laravel، تحقق وحدة تخزين مؤقت على مستوى التطبيق التأثير نفسه مع مخاطر أقل للآثار الجانبية.
كيف أحظر العناوين المتكررة باستخدام fail2ban؟
الحد من المعدل يبطئ؛ والحظر يوقف. يقرأ fail2ban السجلات ويضيف قاعدة جدار حماية ضد العناوين التي تتجاوز الحد كثيرا.
apt install fail2ban
في /etc/fail2ban/jail.local:
[nginx-limit-req]
enabled = true
filter = nginx-limit-req
logpath = /var/log/nginx/error.log
maxretry = 10
findtime = 60
bantime = 3600
systemctl restart fail2ban
fail2ban-client status nginx-limit-req
بعبارة واضحة، تعني القاعدة: العنوان الذي يتجاوز الحد 10 مرات خلال 60 ثانية يحظر لمدة ساعة.
ما الإعدادات التي يجب تجنبها في مواجهة هجوم تطبيقي؟
هذه الإعدادات منتشرة كثيرا، وضررها أكبر من نفعها.
| ما يجب تجنبه | السبب |
|---|---|
حظر curl وwget حسب وكيل المستخدم |
يغير المهاجم الوكيل بسطر واحد. أما أنت فتعطل أدوات المراقبة وwebhooks الدفع وسكربتات النسخ الاحتياطي. |
client_max_body_size 1k |
يفشل كل رفع لصورة، وكل نموذج طويل نسبيا، وكل تحديث لنظام إدارة المحتوى. |
allow على شبكة فرعية ثم deny all |
تغلق الموقع أمام الإنترنت بأكمله، وبالتالي أمام زوارك. خصص هذا النمط لعناوين URL الخاصة بالإدارة. |
| حظر دول بأكملها تلقائيا | الكثير من الإيجابيات الكاذبة، والمهاجمون يستأجرون أجهزة في كل مكان. |
| تعطيل السجلات «من أجل الأداء» | تفقد الوسيلة الوحيدة لفهم ما يحدث لك. |
قاعدة بسيطة: أي إجراء يحظر نطاقا أوسع من النمط المرصود سيكلفك عملاء.
متى يجب فتح تذكرة لدينا؟
عندما تتجاوز المشكلة ما يمكنك معالجته على الجهاز: وصلة متشبعة، أو هجوم طويل، أو آلاف العناوين.
افتح تذكرة إذا:
- ظل الجهاز غير متاح رغم هذه الإعدادات؛
- كانت الحركة تشبع الوصلة الشبكية قبل أن تصل حتى إلى Nginx؛
- استمر الهجوم وكان مصدره آلاف العناوين المختلفة.
أرفق بالتذكرة مقتطفات السجلات من الخطوة 1 والفترة الزمنية المعنية. فهذا ما يتيح لنا التدخل مسبقا، على مستوى الشبكة، حيث لا يمكنك التدخل.