عنوان:

‫ایمن‌سازی حرفه‌ای Nginx: مسدودسازی جغرافیایی و مقابله با حملات (راهنمای ویژه توسعه‌دهندگان .NET)


نویسنده: وحید نصیری
تاریخ: ۱۴۰۴/۱۲/۰۱ ۱۳:۰۰
آدرس: www.dntips.ir
بسیاری از توسعه‌دهندگان دات‌نت (ASP.NET Core) برای میزبانی اپلیکیشن‌های خود در محیط لینوکس، از Nginx به عنوان یک Reverse Proxy قدرتمند در جلوی Kestrel استفاده می‌کنند. در حالی که می‌توان برخی محدودیت‌ها را در سطح Middlewareهای دات‌نت اعمال کرد، اما اصول معماری نرم‌افزار ایجاب می‌کند که ترافیک مخرب و ناخواسته را در همان لایه ورودی (Edge) متوقف کنیم تا منابع سرور و پردازنده درگیر پردازش درخواست‌های بیهوده نشوند.
در این مقاله، راهکاری جامع برای مسدود کردن ترافیک کشورهای خاص بدون استفاده از CDN، پیاده‌سازی Rate Limiting هوشمند و استفاده از Fail2Ban برای دفع حملات فعال را بررسی خواهیم کرد.

بخش اول: مسدودسازی جغرافیایی (Geo-Blocking) با GeoIP2
برای مسدود کردن دسترسی کشورها، روش قدیمی geoip منسوخ شده است. روش استاندارد و مدرن، استفاده از ماژول ngx_http_geoip2_module است.
۱. نصب پیش‌نیازها
ابتدا باید ماژول مربوطه را روی سرور (معمولاً اوبونتو/دبیان) نصب کنید:
sudo apt update
sudo apt install libnginx-mod-http-geoip2 -y

۲. دریافت دیتابیس موقعیت‌یابی
ما به دیتابیس GeoLite2-Country نیاز داریم. این دیتابیس برای تشخیص کشورها دقت بالایی دارد (دقت شهر برای هدف ما نیاز نیست).
sudo mkdir -p /etc/nginx/geoip
cd /etc/nginx/geoip
# دانلود دیتابیس (از میرور گیت‌هاب برای دسترسی سریع)
wget https://github.com/P3TERX/GeoLite.mmdb/raw/download/GeoLite2-Country.mmdb

۳. پیکربندیNginx (nginx.conf)
در فایل تنظیمات اصلی Nginx، باید ماژول را بارگذاری کرده و منطق مسدودسازی را تعریف کنیم.
نکته مهم: ما از کد وضعیت 444 استفاده می‌کنیم. این کد استاندارد HTTP نیست، بلکه مخصوص Nginx است و به سرور می‌گوید "بدون ارسال هیچ پاسخی، کانکشن را قطع کن". این کار دو مزیت دارد:
  • منابع سرور برای تولید صفحه HTML خطا (مثل 403) هدر نمی‌رود.
  • ربات‌ها (Bot) معمولاً روی خطاهای استاندارد تلاش مجدد (Retry) می‌کنند، اما قطع کانکشن آن‌ها را متوقف می‌کند.
فایل /etc/nginx/nginx.conf را ویرایش کنید:
http {
    # ... سایر تنظیمات ...

    # بارگذاری دیتابیس GeoIP2
    geoip2 /etc/nginx/geoip/GeoLite2-Country.mmdb {
        $geoip2_country_code country iso_code;
    }

    # نقشه‌برداری کد کشورها به وضعیت بلاک
    map $geoip2_country_code $block_country {
        default 0; # به صورت پیش‌فرض همه مجاز هستند
        CN 1;      # چین
        RU 1;      # روسیه
        VN 1;      # ویتنام
        # سایر کدهای ISO کشورها را می‌توانید اضافه کنید
    }

    # تعریف Rate Limit عمومی (توضیحات در بخش دوم)
    limit_req_zone $binary_remote_addr zone=global:10m rate=15r/s;
}

۴. اعمال بلاک در Server Block
حالا در تنظیمات سایت خود (مثلاً /etc/nginx/sites-available/default یا فایل کانفیگ پروژه دات‌نت شما)، شرط بررسی را اضافه کنید:
server {
    listen 80;
    server_name yourdomain.com;

    # بلاک کردن کشورها قبل از هر پردازشی
    if ($block_country) {
        return 444; 
    }

    location / {
        # اعمال محدودیت نرخ درخواست
        limit_req zone=global burst=30 nodelay;
        
        # تنظیمات پروکسی به سمت Kestrel
        proxy_pass http://localhost:5000;
        # ... سایر تنظیمات پروکسی ...
    }
}

بخش دوم: اهمیت Rate Limiting
حتی اگر ترافیک کشورهای پرخطر را مسدود کنید، حملات می‌توانند از کشورهای مجاز (حتی کشور خودتان) انجام شوند. بنابراین، Rate Limiting یک لایه دفاعی ضروری است.
در کد بالا، ما از تنظیمات زیر استفاده کردیم:
  • rate=15r/s: اجازه ۱۵ درخواست در ثانیه به ازای هر IP.
  • burst=30: اجازه می‌دهد ترافیک ناگهانی تا ۳۰ درخواست در صف قرار گیرد.
  • nodelay: درخواست‌های موجود در burst را بدون وقفه پردازش می‌کند (تجربه کاربری بهتر برای کاربران واقعی)، اما اگر از حد بگذرد، خطا می‌دهد.

بخش سوم: آپدیت خودکار دیتابیس (بدون Downtime)
دیتابیس IPها مرتب تغییر می‌کند. برای به‌روزرسانی آن بدون متوقف کردن سرویس Nginx، از یک اسکریپت Bash هوشمند استفاده می‌کنیم.
۱. ایجاد اسکریپت
فایل /usr/local/bin/update-geoip.sh را ایجاد کنید:
#!/usr/bin/env bash
set -euo pipefail

# تنظیم متغیرها
GEOIP_DIR="/etc/nginx/geoip"
TMP_DIR="$(mktemp -d)"
DB_NAME="GeoLite2-Country.mmdb"
URL="https://github.com/P3TERX/GeoLite.mmdb/raw/download/${DB_NAME}"
LOG="/var/log/geoip-update.log"

echo "[$(date)] Starting GeoIP update" >> "$LOG"

# دانلود در پوشه موقت
cd "$TMP_DIR"
wget -q --timeout=30 "$URL"

# Sanity Check: بررسی اینکه فایل دانلود شده سالم و غیرخالی باشد
if [ ! -s "$DB_NAME" ]; then
  echo "[$(date)] Download failed or empty file" >> "$LOG"
  exit 1
fi

# جایگزینی اتمیک (Atomic Replace)
# این روش تضمین می‌کند که Nginx هرگز فایل ناقص یا خراب را نمی‌خواند
mv "$DB_NAME" "$GEOIP_DIR/${DB_NAME}.new"
mv "$GEOIP_DIR/${DB_NAME}.new" "$GEOIP_DIR/$DB_NAME"

# بارگذاری مجدد سرویس (Reload نه Restart)
nginx -t && systemctl reload nginx

echo "[$(date)] GeoIP update successful" >> "$LOG"
rm -rf "$TMP_DIR"

۲. فعال‌سازی و زمان‌بندی
به اسکریپت دسترسی اجرا داده و آن را در Cron Job تنظیم کنید (هفته‌ای یکبار کافی است):
sudo chmod +x /usr/local/bin/update-geoip.sh
sudo crontab -e
خط زیر را به انتهای فایل اضافه کنید (اجرا در ساعت ۳ صبح دوشنبه‌ها):
0 3 * * 1 /usr/local/bin/update-geoip.sh

بخش چهارم: دفاع فعال با Fail2Ban (لایه تکمیلی)
Nginx به تنهایی فقط قوانین را اجرا می‌کند، اما Fail2Ban رفتار لاگ‌ها را رصد کرده و مهاجمین را در سطح فایروال (iptables) بلاک می‌کند. این ترکیب (Nginx 444 + Fail2Ban) بسیار قدرتمند است.
ما سه نوع حمله رایج را هدف قرار می‌دهیم:
  • Rate Limit Violations: کسانی که بیش از حد درخواست می‌فرستند.
  • Bot Scanners: ربات‌هایی که دنبال فایل‌های wp-login.php, .env, .git و... می‌گردند.
  • 4xx Floods: تلاش‌های Brute-force که منجر به خطاهای ۴۰۳ یا ۴۰۴ پیاپی می‌شود.

۱. تعریف فیلترها
فایل‌های زیر را در مسیر /etc/fail2ban/filter.d/ ایجاد کنید.
فیلتر ۱: درخواست‌های محدود شده (nginx-req-limit.conf)
[Definition]
failregex = limiting requests, excess:.* by zone.*client: <HOST>
ignoreregex =

فیلتر ۲: ربات‌های مخرب (nginx-custom-bot.conf)
این فیلتر هوشمندانه درخواست‌های مشکوک به فایل‌های حساس را که منجر به خطا شده‌اند شناسایی می‌کند.
[Definition]
failregex = ^<HOST> - .* "(GET|POST).*?(wp-login\.php|xmlrpc\.php|\.env|\.git|phpmyadmin).*" (404|403|444)
ignoreregex =

فیلتر ۳: سیل خطاهای سری ۴۰۰ (nginx-4xx.conf)
[Definition]
failregex = ^<HOST> - .* "(GET|POST).*" 4(03|04|44)
ignoreregex =

۲. تنظیم Jail نهایی
فایل /etc/fail2ban/jail.d/nginx.conf را ایجاد کنید. در اینجا سیاست‌های سخت‌گیرانه‌ای برای محیط Production اعمال می‌کنیم.
[nginx-req-limit]
enabled  = true
filter   = nginx-req-limit
port     = http,https
logpath  = /var/log/nginx/error.log
maxretry = 3
findtime = 60
bantime  = 1d   # بن شدن به مدت یک روز

[nginx-custom-bot]
enabled  = true
filter   = nginx-custom-bot
port     = http,https
logpath  = /var/log/nginx/access.log
maxretry = 2    # حساسیت بالا: با دومین تلاش بن می‌شود
findtime = 10m
bantime  = 7d   # بن طولانی مدت (یک هفته)

[nginx-4xx]
enabled  = true
filter   = nginx-4xx
port     = http,https
logpath  = /var/log/nginx/access.log
maxretry = 10
findtime = 1m
bantime  = 6h

۳. اعمال تغییرات
systemctl restart fail2ban

نتیجه‌گیری و نکات پایانی
با پیاده‌سازی این معماری، سرور شما در سه لایه محافظت می‌شود:
  • GeoIP: ترافیک کشورهای پرخطر اصلاً پردازش نمی‌شود.
  • Nginx Config: درخواست‌های غیرمجاز با کد ۴۴۴ (Close Connection) قطع می‌شوند.
  • Fail2Ban: IPهای مهاجم که از سدها عبور کرده یا رفتار مشکوک دارند، در سطح فایروال سیستم‌عامل بلاک می‌شوند.

چند توصیه تجربی:
  • مانیتورینگ: هر از گاهی وضعیت Fail2Ban را با دستور fail2ban-client status nginx-custom-bot چک کنید تا ببینید چه تعداد حمله دفع شده است.
  • مدت زمان بن (Bantime): هرگز bantime = -1 (بن دائم) قرار ندهید؛ زیرا به مرور زمان دیتابیس Fail2Ban بسیار حجیم و کند می‌شود. بن‌های طولانی (مثلاً یک هفته) کافی و موثر هستند.
  • تست نهایی: همیشه بعد از تغییرات Nginx دستور nginx -t را اجرا کنید تا از صحت Syntax مطمئن شوید.

این تنظیمات یک دژ مستحکم برای اپلیکیشن‌های دات‌نت شما فراهم می‌کند و به شما اجازه می‌دهد با خیال راحت روی توسعه Business Logic تمرکز کنید.