Nginx ile ModSecurity WAF Entegrasyonu

Web uygulamalarını korumak artık opsiyonel değil, zorunlu. Özellikle production ortamında çalışan bir Nginx sunucusu varsa ve önünde bir WAF (Web Application Firewall) yoksa, SQL injection’dan XSS’e, path traversal’dan remote code execution’a kadar onlarca saldırı vektörüne karşı savunmasız kalıyorsunuz. ModSecurity, bu noktada devreye giriyor ve Nginx ile entegre edildiğinde güçlü bir savunma katmanı oluşturuyor.

Bu yazıda ModSecurity’yi Nginx ile nasıl entegre edeceğimizi, OWASP Core Rule Set’i nasıl yapılandıracağımızı ve gerçek dünya senaryolarında nasıl fine-tune yapacağımızı adım adım ele alacağız.

ModSecurity Nedir ve Neden Nginx ile Kullanmalıyız

ModSecurity başlangıçta Apache için geliştirilmiş bir WAF modülüydü. Zamanla standalone bir kütüphane olan libmodsecurity (ModSecurity v3) haline getirildi ve bu sayede Nginx, IIS gibi farklı web sunucularıyla da kullanılabilir hale geldi.

Nginx için ModSecurity kullanımının temel avantajları şunlardır:

  • OWASP Top 10 koruması: SQL injection, XSS, CSRF gibi yaygın saldırılara karşı hazır kurallar
  • Real-time trafik analizi: İstekleri sunucuya ulaşmadan önce inceleme
  • Özelleştirilebilir kurallar: Kendi iş mantığınıza uygun kurallar yazabilme
  • Detaylı loglama: Hangi isteğin neden engellendiğini kayıt altına alma
  • Detection ve Prevention modları: Önce detect, sonra block mantığıyla çalışma

Nginx native olarak ModSecurity desteğiyle gelmiyor. Bunun için ModSecurity-nginx connector adlı bir dinamik modül kullanıyoruz. Kurulum süreci biraz uzun ama bir kez yapıldığında bakımı oldukça kolay.

Sistem Hazırlığı ve Bağımlılıkların Kurulumu

Ubuntu 22.04 LTS üzerinde çalışacağız. Önce sistemi güncelleyip gerekli bağımlılıkları yükleyelim:

sudo apt update && sudo apt upgrade -y

sudo apt install -y 
  build-essential 
  libpcre3 
  libpcre3-dev 
  libssl-dev 
  libtool 
  autoconf 
  automake 
  git 
  libcurl4-openssl-dev 
  libgeoip-dev 
  liblmdb-dev 
  libmaxminddb-dev 
  libxml2-dev 
  libyajl-dev 
  pkgconf 
  zlib1g-dev

Şu an sisteminizde çalışan Nginx sürümünü not edin, çünkü connector modülünü bu sürümle derleyeceğiz:

nginx -v
# nginx version: nginx/1.24.0

# Nginx'in derleme parametrelerini de görün
nginx -V 2>&1 | grep configure

Bu çıktı önemli. Modülü derlerken aynı configure argümanlarını kullanmamız gerekiyor, aksi takdirde modül yüklenmez.

libmodsecurity (ModSecurity v3) Derleme

ModSecurity’nin kaynak kodunu indirip derleyeceğiz:

cd /usr/local/src

# ModSecurity kaynak kodunu çek
sudo git clone --depth 1 -b v3/master --single-branch 
  https://github.com/SpiderLabs/ModSecurity

cd ModSecurity

# Submodule'leri başlat
sudo git submodule init
sudo git submodule update

# Derleme hazırlığı
sudo ./build.sh
sudo ./configure

# Derle ve kur (bu adım 10-15 dakika sürebilir)
sudo make -j$(nproc)
sudo make install

Kurulum başarılıysa /usr/local/modsecurity/ dizininde kütüphane dosyalarını göreceksiniz:

ls /usr/local/modsecurity/lib/
# libmodsecurity.a  libmodsecurity.la  libmodsecurity.so  libmodsecurity.so.3

ModSecurity-Nginx Connector Modülünü Derleme

Şimdi Nginx connector’ını indirip Nginx ile birlikte derleyeceğiz. Burada dikkat edilmesi gereken nokta: Nginx’i yeniden derlemiyoruz, sadece dinamik modülü derliyoruz.

cd /usr/local/src

# Connector'ı indir
sudo git clone --depth 1 
  https://github.com/SpiderLabs/ModSecurity-nginx.git

# Mevcut Nginx sürümünü indir (sisteminizle aynı sürüm olmalı!)
NGINX_VERSION=$(nginx -v 2>&1 | grep -oP '(?<=nginx/)[0-9.]+')
sudo wget http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz
sudo tar xvzf nginx-${NGINX_VERSION}.tar.gz

cd nginx-${NGINX_VERSION}

# Nginx derleme argümanlarını al ve modülü ekleyerek derle
NGINX_ARGS=$(nginx -V 2>&1 | grep "configure arguments:" | cut -d: -f2-)

sudo ./configure ${NGINX_ARGS} 
  --add-dynamic-module=/usr/local/src/ModSecurity-nginx

sudo make modules

# Modülü Nginx modules dizinine kopyala
sudo cp objs/ngx_http_modsecurity_module.so /etc/nginx/modules-enabled/

OWASP Core Rule Set Kurulumu

ModSecurity tek başına çok işe yaramaz, kurallara ihtiyacı var. OWASP CRS (Core Rule Set) bu iş için endüstri standardı:

cd /etc/nginx

# ModSecurity için dizin oluştur
sudo mkdir -p modsec

# Varsayılan yapılandırma dosyasını kopyala
sudo cp /usr/local/src/ModSecurity/modsecurity.conf-recommended 
  /etc/nginx/modsec/modsecurity.conf

# Unicode mapping dosyasını kopyala
sudo cp /usr/local/src/ModSecurity/unicode.mapping 
  /etc/nginx/modsec/

# OWASP CRS'i indir
cd /etc/nginx/modsec

sudo git clone https://github.com/coreruleset/coreruleset.git 
  /etc/nginx/modsec/owasp-crs

# CRS yapılandırma dosyasını oluştur
sudo cp /etc/nginx/modsec/owasp-crs/crs-setup.conf.example 
  /etc/nginx/modsec/owasp-crs/crs-setup.conf

Şimdi ana ModSecurity yapılandırma dosyasını düzenleyelim. Detection modunu önce DetectionOnly olarak ayarlıyoruz. Bir süre izledikten sonra On yapacağız:

sudo sed -i 's/SecRuleEngine DetectionOnly/SecRuleEngine On/' 
  /etc/nginx/modsec/modsecurity.conf

# Aslında ilk başta DetectionOnly ile başlamak daha mantıklı
# Production'a almadan önce logları izleyin
sudo sed -i 's/SecRuleEngine On/SecRuleEngine DetectionOnly/' 
  /etc/nginx/modsec/modsecurity.conf

Ana ModSecurity include dosyasını oluşturalım:

sudo tee /etc/nginx/modsec/main.conf << 'EOF'
# ModSecurity Ana Yapılandırması
Include /etc/nginx/modsec/modsecurity.conf

# OWASP CRS Yapılandırması
Include /etc/nginx/modsec/owasp-crs/crs-setup.conf

# OWASP CRS Kuralları
Include /etc/nginx/modsec/owasp-crs/rules/*.conf
EOF

Nginx Yapılandırmasına ModSecurity Ekleme

Nginx’in modülü yüklemesi için nginx.conf dosyasının en üstüne şu satırı ekleyelim:

sudo tee -a /etc/nginx/nginx.conf.head << 'EOF'
load_module modules/ngx_http_modsecurity_module.so;
EOF

Aslında bunu doğrudan nginx.conf dosyasının ilk satırına eklemek daha temiz:

sudo nano /etc/nginx/nginx.conf

Dosyanın başına şunu ekleyin:

load_module modules/ngx_http_modsecurity_module.so;

events {
    worker_connections 1024;
}

http {
    # ModSecurity'yi global olarak aktif et
    modsecurity on;
    modsecurity_rules_file /etc/nginx/modsec/main.conf;
    
    # Diğer http direktifleri...
}

Belirli bir virtual host için etkinleştirmek daha yaygın bir yaklaşım. Örneğin bir e-ticaret sitesi için:

server {
    listen 443 ssl http2;
    server_name shop.example.com;

    ssl_certificate /etc/ssl/certs/shop.example.com.crt;
    ssl_certificate_key /etc/ssl/private/shop.example.com.key;

    # ModSecurity bu server block için aktif
    modsecurity on;
    modsecurity_rules_file /etc/nginx/modsec/main.conf;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    # API endpoint'i için ModSecurity'yi kapat (performans için)
    location /api/internal/health {
        modsecurity off;
        proxy_pass http://127.0.0.1:8080;
    }
}

Yapılandırmayı test edip Nginx’i yeniden başlatalım:

sudo nginx -t
sudo systemctl restart nginx
sudo systemctl status nginx

ModSecurity’yi Test Etme

Kurulumun çalışıp çalışmadığını test edelim. Klasik bir SQL injection denemesi yapalım:

# SQL injection testi
curl -I "http://localhost/?id=1+AND+1=1"

# XSS testi
curl -I "http://localhost/?q=<script>alert('xss')</script>"

# Path traversal testi
curl -I "http://localhost/?file=../../etc/passwd"

# Nikto ile genel tarama (test ortamında)
nikto -h http://localhost

Detection modundayken bunlar engellenmiyor ama loglanıyor. Logları kontrol edelim:

# ModSecurity audit logları
sudo tail -f /var/log/modsec_audit.log

# Nginx error logunda ModSecurity mesajları
sudo tail -f /var/log/nginx/error.log | grep -i "ModSecurity"

Bir audit log girdisi şöyle görünür:

--abc123--
[01/Jan/2024:10:30:45 +0300] "GET /?id=1+AND+1=1 HTTP/1.1" 200
ModSecurity: Warning. Matched "Operator `Rx' with parameter `(?i:[s(]...
[id "942100"][file "REQUEST-942-APPLICATION-ATTACK-SQLI.conf"]
[line "45"][rev ""][msg "SQL Injection Attack Detected via libinjection"]
[data "Matched Data: 1 AND 1=1 found within ARGS:id: 1 AND 1=1"]
[severity "CRITICAL"][ver "OWASP_CRS/3.3.4"][tag "application-multi"]

Gerçek Dünya Senaryosu: False Positive Yönetimi

Production’daki en büyük sorun false positive’ler. Bir müşterinin WordPress sitesini ModSecurity arkasına aldığımızda karşılaştığımız tipik sorunlar şunlardı: Admin paneli çalışmıyordu, medya yüklemeleri engelleniyordu, bazı plugin’ler hata veriyordu.

Hangi kuralın hangi isteği engellediğini bulmak için:

# Audit logdan false positive'leri analiz et
sudo grep "id "9" /var/log/modsec_audit.log | 
  grep -oP 'id "K[0-9]+' | 
  sort | uniq -c | sort -rn | head -20

WordPress admin için özel bir exclusion dosyası oluşturalım:

sudo tee /etc/nginx/modsec/wordpress-exclusions.conf << 'EOF'
# WordPress Admin için ModSecurity Exclusions

# WordPress post editor - HTML içerik gönderimi
SecRule REQUEST_URI "@beginsWith /wp-admin/post.php" 
  "id:9001,phase:1,pass,nolog,
  ctl:ruleRemoveById=941100-941999,
  ctl:ruleRemoveById=942100-942999"

# WordPress media upload
SecRule REQUEST_URI "@beginsWith /wp-admin/upload.php" 
  "id:9002,phase:1,pass,nolog,
  ctl:ruleRemoveById=200002"

# WooCommerce checkout - kredi kartı alanları
SecRule REQUEST_URI "@beginsWith /checkout" 
  "id:9003,phase:1,pass,nolog,
  ctl:ruleRemoveTargetById=942440;REQUEST_BODY"

# XML-RPC (kapalıysa bu satırı silin)
SecRule REQUEST_URI "@beginsWith /xmlrpc.php" 
  "id:9004,phase:1,pass,nolog,
  ctl:ruleEngine=Off"
EOF

Bu dosyayı main.conf‘a ekleyelim:

echo 'Include /etc/nginx/modsec/wordpress-exclusions.conf' | 
  sudo tee -a /etc/nginx/modsec/main.conf

Paranoia Level Ayarı

OWASP CRS’in dört paranoia seviyesi var. Her seviyede ek kurallar devreye giriyor:

  • PL1: Varsayılan, çoğu site için uygun, düşük false positive
  • PL2: Daha agresif, bazı özel karakterler için alarm
  • PL3: Oldukça kısıtlayıcı, büyük olasılıkla false positive’ler olacak
  • PL4: En yüksek güvenlik, neredeyse her şeyi şüpheyle karşılar

crs-setup.conf dosyasında paranoia level’ı ayarlayın:

sudo sed -i 's/.*SecAction "id:900000.*/SecAction \n  "id:900000,\n   phase:1,\n   nolog,\n   pass,\n   t:none,\n   setvar:tx.paranoia_level=2"/' 
  /etc/nginx/modsec/owasp-crs/crs-setup.conf

Ya da direkt dosyayı açıp şu satırı ekleyin:

SecAction 
  "id:900000,
   phase:1,
   nolog,
   pass,
   t:none,
   setvar:tx.paranoia_level=1,
   setvar:tx.detection_paranoia_level=2"

Bu yapılandırmayla enforcement PL1, detection PL2 olarak ayarlanmış oluyor. Yani PL2 kuralları sadece loglanıyor ama engellenmiyor. Fine-tune için ideal bir yaklaşım.

Rate Limiting ile ModSecurity Entegrasyonu

ModSecurity’yi Nginx’in yerleşik rate limiting özelliğiyle birleştirince çok güçlü bir kombinasyon elde ediyorsunuz:

http {
    # Rate limiting zone tanımla
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
    limit_req_zone $binary_remote_addr zone=login_limit:10m rate=5r/m;

    server {
        listen 443 ssl;
        server_name api.example.com;

        modsecurity on;
        modsecurity_rules_file /etc/nginx/modsec/main.conf;

        # API endpoint'leri için rate limiting
        location /api/ {
            limit_req zone=api_limit burst=20 nodelay;
            limit_req_status 429;
            
            proxy_pass http://backend;
        }

        # Login brute force koruması
        location /auth/login {
            limit_req zone=login_limit burst=3 nodelay;
            limit_req_status 429;
            
            modsecurity on;
            proxy_pass http://backend;
        }
    }
}

ModSecurity tarafında da brute force kuralı ekleyelim:

sudo tee /etc/nginx/modsec/custom-rules.conf << 'EOF'
# Brute Force Koruması
SecRule IP:BF_COUNTER "@gt 10" 
  "id:10001,
   phase:2,
   deny,
   status:429,
   log,
   msg:'Brute Force Saldirisi Algilandi',
   tag:'BRUTEFORCE'"

SecRule REQUEST_URI "@streq /auth/login" 
  "id:10002,
   phase:5,
   pass,
   nolog,
   setvar:ip.BF_COUNTER=+1,
   expirevar:ip.BF_COUNTER=300"

# IP whitelist - ofis IP'leri
SecRule REMOTE_ADDR "@ipMatch 10.0.0.0/8,192.168.1.0/24" 
  "id:10003,
   phase:1,
   allow,
   nolog,
   msg:'Guvenilir IP - ModSecurity Bypass'"
EOF

echo 'Include /etc/nginx/modsec/custom-rules.conf' | 
  sudo tee -a /etc/nginx/modsec/main.conf

ModSecurity’yi Production’a Alma

Şimdiye kadar DetectionOnly modunda çalıştırdık. Production’a almadan önce şu kontrolleri yapın:

# 1. Son bir haftanın loglarını analiz et
sudo awk '/id "9[0-9]{5}"/{match($0, /id "[0-9]+"/, arr); print arr[0]}' 
  /var/log/modsec_audit.log | 
  sort | uniq -c | sort -rn

# 2. Kaç unique IP engelleniyor?
sudo grep "Access denied" /var/log/nginx/error.log | 
  grep -oP 'client: K[d.]+' | 
  sort -u | wc -l

# 3. Hangi URL'ler en çok tetikleniyor?
sudo grep "ModSecurity" /var/log/nginx/error.log | 
  grep -oP 'request: "K[^"]+' | 
  sort | uniq -c | sort -rn | head -20

Analizden sonra Prevention moduna geçin:

sudo sed -i 
  's/SecRuleEngine DetectionOnly/SecRuleEngine On/' 
  /etc/nginx/modsec/modsecurity.conf

sudo nginx -t && sudo systemctl reload nginx

Performans Optimizasyonu

ModSecurity her isteği analiz ettiği için CPU maliyeti var. Optimize etmek için:

sudo tee -a /etc/nginx/modsec/modsecurity.conf << 'EOF'

# Performans Optimizasyonu
SecRequestBodyLimit 13107200
SecRequestBodyNoFilesLimit 131072
SecResponseBodyLimit 524288

# Statik dosyalar için ModSecurity'yi devre dışı bırak
# (Nginx config'inde location bazlı yap)

# Audit log sadece engellenen istekler için
SecAuditEngine RelevantOnly
SecAuditLogRelevantStatus "^(?:5|4(?!04))"

# Paralel işleme
SecPcreMatchLimit 100000
SecPcreMatchLimitRecursion 100000
EOF

Nginx tarafında da statik dosyaları ModSecurity’den muaf tutun:

# Statik dosyalar için WAF kapalı
location ~* .(jpg|jpeg|png|gif|ico|css|js|woff|woff2|ttf|svg)$ {
    modsecurity off;
    expires 30d;
    add_header Cache-Control "public, no-transform";
    root /var/www/html;
}

Log Monitoring ve Alerting

ModSecurity loglarını izlemek için basit bir script yazalım:

sudo tee /usr/local/bin/modsec-monitor.sh << 'EOF'
#!/bin/bash

LOG_FILE="/var/log/modsec_audit.log"
ALERT_THRESHOLD=50
ADMIN_EMAIL="[email protected]"

# Son 1 saatteki engellenen istek sayısı
BLOCKED=$(grep "$(date -d '1 hour ago' '+%d/%b/%Y:%H')" "$LOG_FILE" | 
  grep "Access denied" | wc -l)

if [ "$BLOCKED" -gt "$ALERT_THRESHOLD" ]; then
  echo "UYARI: Son 1 saatte $BLOCKED istek engellendi!" | 
    mail -s "ModSecurity Alert - $(hostname)" "$ADMIN_EMAIL"
  
  # En cok saldiran IP'leri logla
  echo "Top 10 Saldirgan IP:" >> /var/log/modsec-alerts.log
  grep "$(date -d '1 hour ago' '+%d/%b/%Y:%H')" "$LOG_FILE" | 
    grep -oP 'client: K[d.]+' | 
    sort | uniq -c | sort -rn | head -10 >> /var/log/modsec-alerts.log
fi
EOF

sudo chmod +x /usr/local/bin/modsec-monitor.sh

# Cron'a ekle - her saat çalışsın
echo "0 * * * * root /usr/local/bin/modsec-monitor.sh" | 
  sudo tee /etc/cron.d/modsec-monitor

Yaygın Sorunlar ve Çözümleri

Kurulum ve yapılandırma sırasında sıkça karşılaşılan sorunlar:

Modül yüklenmiyor: Nginx binary’sini farklı bir user derlemiş olabilirsiniz veya sürüm uyumsuzluğu var. nginx -V çıktısındaki exact sürümle modülü derlediğinizden emin olun.

Yüksek CPU kullanımı: SecAuditEngine ayarını RelevantOnly yapın ve statik dosyalar için ModSecurity’yi kapatın. PL1’den PL2’ye geçmek de CPU kullanımını önemli ölçüde artırır.

WordPress admin çalışmıyor: Rule ID 941100-941999 ve 942100-942999 aralıklarını /wp-admin için disable edin. Yukarıdaki exclusion dosyasını kullanın.

API çağrıları engelleniyor: JSON body’lerde özel karakterler CRS’i tetikleyebilir. Content-Type: application/json için ctl:ruleRemoveById=200001 ekleyin.

Audit log disk dolduruyor: SecAuditLogParts direktifiyle loglanacak bölümleri sınırlayın. Minimal log için SecAuditLogParts ABIJDEFHZ kullanın.

Sonuç

ModSecurity ve OWASP CRS kombinasyonu, Nginx tabanlı altyapınız için sağlam bir WAF katmanı oluşturuyor. Ancak “kur ve unut” bir çözüm değil. False positive’leri düzenli olarak gözden geçirmeniz, CRS güncellemelerini takip etmeniz ve kurallarınızı uygulamanızın davranışına göre fine-tune etmeniz gerekiyor.

Kademeli bir yaklaşım her zaman daha sağlıklı: Önce DetectionOnly modunda en az bir hafta izleyin, logları analiz edin, exclusion’larınızı yazın, sonra Prevention moduna geçin. PL1 ile başlayıp sisteminizin stabilitesini doğruladıktan sonra PL2’ye geçmeyi değerlendirin.

Gerçek dünyada ModSecurity tek başına yeterli değil. Nginx rate limiting, Fail2Ban ile IP banlama, CDN düzeyinde DDoS koruması ve düzenli uygulama güvenlik testleriyle birlikte bir katmanlı güvenlik mimarisi oluşturmanız gerekiyor. ModSecurity bu mimaride önemli bir halka, ama tek halka değil.

Son olarak: CRS güncellemelerini takip edin. git pull ile kuralları güncel tutmak, zero-day’lere karşı korunmanın en pratik yolu.

Yorum yapın