Nginx Access Log Analizi ile Saldırı Tespiti

Sunucunuzun access loglarına düzenli olarak göz atmıyorsanız, kör uçuş yapıyorsunuz demektir. Nginx access logları, sisteminize yapılan her isteğin kaydını tutar ve bu kayıtlar içinde saldırı girişimlerini, bot trafiğini, brute force denemelerini ve çok daha fazlasını görebilirsiniz. Bu yazıda, Nginx access loglarını nasıl analiz edeceğinizi, saldırı tespiti için hangi komutları kullanacağınızı ve tespit ettiğiniz tehditlere karşı nasıl aksiyon alacağınızı ele alacağız.

Nginx Access Log Formatını Anlamak

Analiz yapmadan önce, neye baktığımızı bilmemiz gerekiyor. Nginx’in varsayılan access log formatı şu şekildedir:

# /etc/nginx/nginx.conf içindeki varsayılan log_format
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                '$status $body_bytes_sent "$http_referer" '
                '"$http_user_agent" "$http_x_forwarded_for"';

Örnek bir log satırı şöyle görünür:

192.168.1.100 - - [15/Jan/2025:14:32:01 +0300] "GET /wp-admin/login.php HTTP/1.1" 200 4523 "-" "Mozilla/5.0 (compatible; Googlebot/2.1)"

Bu satırda sırasıyla şunlar var:

  • $remote_addr: İstek yapan IP adresi
  • $time_local: İsteğin zamanı
  • $request: HTTP metodu, URL ve protokol
  • $status: HTTP durum kodu
  • $body_bytes_sent: Gönderilen byte miktarı
  • $http_referer: Referer header
  • $http_user_agent: Kullanıcı ajanı

Daha detaylı analiz için log formatınıza $request_time ve $upstream_response_time eklemek iyi bir pratiktir. Bu sayede yavaş istekleri de takip edebilirsiniz.

Temel Log Analiz Komutları

En Çok İstek Yapan IP Adreslerini Bulmak

Saldırı tespitinde ilk adım genellikle en çok istek yapan IP adreslerini bulmaktır. Tek bir IP’den gelen anormal miktarda istek, brute force, DDoS veya tarama girişimine işaret eder.

# En çok istek yapan ilk 20 IP adresini listele
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

Bu çıktıda 10.000’in üzerinde istek yapan bir IP görüyorsanız, ciddi bir sorunla karşı karşıyasınız demektir. Normal bir kullanıcı birkaç saatte bu kadar istek yapmaz.

Belirli Bir Zaman Dilimindeki İstekleri Filtrelemek

Gerçek dünyada loglar çok büyük olabilir. Belirli bir zaman dilimine odaklanmak analizi hızlandırır:

# Bugünün belirli bir saatindeki istekleri filtrele
grep "15/Jan/2025:14:" /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -20

# Son 1 saatteki istekleri analiz et
awk -v d="$(date -d '1 hour ago' +'%d/%b/%Y:%H')" '$0 ~ d' /var/log/nginx/access.log | wc -l

HTTP Durum Kodlarının Dağılımı

HTTP durum kodları, saldırı tipini anlamada kritik rol oynar. Çok sayıda 404 hatası tarama girişimine, çok sayıda 401/403 ise brute force’a işaret edebilir:

# Durum kodlarının dağılımını göster
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn

# Sadece hata kodlarını (4xx ve 5xx) filtrele
awk '$9 ~ /^[45]/' /var/log/nginx/access.log | awk '{print $9}' | sort | uniq -c | sort -rn

Saldırı Tiplerini Tespit Etme

Brute Force Saldırılarını Tespit Etme

WordPress, phpMyAdmin, SSH over web veya özel login sayfalarına yönelik brute force saldırıları çok yaygındır. Bu saldırılar genellikle tek bir IP’den aynı URL’e çok sayıda POST isteği olarak görünür:

# Login sayfasına yapılan POST isteklerini IP bazında say
grep "POST /wp-login.php" /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -20

# Belirli bir saatte 50'den fazla başarısız login denemesi yapan IP'leri bul
grep "POST /wp-login.php" /var/log/nginx/access.log | grep " 401| 403| 200" | awk '{print $1}' | sort | uniq -c | sort -rn | awk '$1 > 50 {print $0}'

# Admin paneline yönelik 429 (Too Many Requests) yanıtlarını izle
grep "429" /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn

Gerçek bir senaryoda karşılaştığım durumu paylaşayım: Bir e-ticaret sitesinin loglarını incelerken, Romanya’dan bir IP adresinin 2 saat içinde /admin/login endpoint’ine 15.000 POST isteği yaptığını gördüm. Tüm istekler farklı parola kombinasyonlarıyla geliyordu. Durum kodu 401 döndüğü için başarılı olamadılar ama sunucuyu ciddi şekilde zorladılar.

Dizin Tarama ve Güvenlik Açığı Araştırması Tespiti

Saldırganlar sisteminize girmeden önce genellikle otomatik araçlarla (nikto, dirb, gobuster gibi) güvenlik açıklarını ve gizli dizinleri tarar. Bu taramalar log’da karakteristik bir iz bırakır:

# Tek bir IP'den gelen 404 yanıtlarının yoğunluğunu kontrol et
awk '$9 == 404 {print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

# Bilinen güvenlik tarama aracı user-agent'larını tespit et
grep -i -E "nikto|sqlmap|nmap|masscan|zgrab|dirbuster|gobuster|nuclei|burpsuite" /var/log/nginx/access.log | awk '{print $1}' | sort -u

# Şüpheli path'lere yapılan istekleri bul
grep -E "(/etc/passwd|/proc/self|.env|.git/|wp-config.php|phpinfo.php|shell.php|c99.php)" /var/log/nginx/access.log | awk '{print $1, $7}' | sort | uniq -c | sort -rn

SQL Injection ve XSS Denemelerini Tespit Etme

Web uygulaması saldırılarında en yaygın teknikler SQL Injection ve Cross-Site Scripting’dir. Bu denemeler URL parametrelerinde veya POST body’sinde belirgin kalıplar bırakır:

# URL'deki şüpheli SQL injection kalıplarını ara
grep -i -E "(union.*select|select.*from|insert.*into|drop.*table|'.*or.*'|--s|;.*--|0x[0-9a-f]+)" /var/log/nginx/access.log | awk '{print $1, $7}' | head -30

# XSS denemelerini tespit et
grep -i -E "(<script|javascript:|onerror=|onload=|alert(|document.cookie)" /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn

# URL encoded saldırı kalıplarını da yakala
grep -E "(%27|%22|%3C|%3E|%2527)" /var/log/nginx/access.log | awk '{print $1, $7}' | head -20

Bot Trafiği ve Crawler Tespiti

Her bot trafiği kötü niyetli değildir, ama meşru görünen user-agent’larla gelen zararlı botlar var. Asıl sorun, zararlı botları meşru olanlardan ayırt etmektir:

# User-agent bazında istek sayısını listele
awk -F'"' '{print $6}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -30

# Boş user-agent ile gelen istekleri say (çoğunlukla bot veya tarama)
awk -F'"' '$6 == "-" {print $0}' /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn

# Curl ve wget ile gelen istekleri tespit et
grep -i -E '"(curl|wget|python-requests|go-http|java/|okhttp)' /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -20

Gelişmiş Analiz Senaryoları

Dakika Bazında İstek Hızı Analizi

DDoS saldırılarında veya agresif taramalarda, bir IP’nin dakika başına yaptığı istek sayısı anormal derecede yükselir. Bunu tespit etmek için:

# Belirli bir IP'nin dakika bazında istek sayısını hesapla
grep "^185.234.219.45" /var/log/nginx/access.log | awk '{print $4}' | cut -c2-18 | sort | uniq -c | sort -rn | head -20

# Son 5 dakikada en aktif 10 IP'yi bul (gerçek zamanlı izleme için)
tail -n 50000 /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10

Büyük Yanıt Boyutlarını İzleme

Bazı saldırılar (özellikle veri sızdırma girişimleri) anormal büyüklükte yanıtlar döndürür. Yüksek byte sayısına dikkat etmek gerekir:

# 1MB'dan büyük yanıt dönen istekleri listele
awk '$10 > 1048576 {print $1, $7, $10}' /var/log/nginx/access.log | sort -k3 -rn | head -20

# Ortalama yanıt boyutunun çok üzerindeki istekleri bul
awk '{sum += $10; count++} END {print "Ortalama:", sum/count, "bytes"}' /var/log/nginx/access.log

Referer Analizi ile Spam ve Fake Traffic Tespiti

# En çok görülen referer'ları listele
awk -F'"' '{print $4}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

# Şüpheli veya spam referer'ları tespit et
awk -F'"' '$4 != "-" {print $4}' /var/log/nginx/access.log | grep -v "yourdomain.com" | sort | uniq -c | sort -rn | head -20

GoAccess ile Görsel Log Analizi

Komut satırı araçları güçlüdür ama GoAccess ile log analizini görsel hale getirebilirsiniz. Kurulum ve kullanım:

# Ubuntu/Debian'da kurulum
apt-get install goaccess

# Gerçek zamanlı terminal arayüzü
goaccess /var/log/nginx/access.log --log-format=COMBINED

# HTML rapor oluştur
goaccess /var/log/nginx/access.log --log-format=COMBINED -o /var/www/html/report.html

# Gerçek zamanlı HTML rapor (websocket ile)
goaccess /var/log/nginx/access.log --log-format=COMBINED -o /var/www/html/report.html --real-time-html --ws-url=wss://yourdomain.com:7890

GoAccess size en çok istek yapan IP’leri, en çok ziyaret edilen sayfaları, durum kodu dağılımını ve çok daha fazlasını güzel bir arayüzde gösterir.

Saldırı Tespit Sonrası Aksiyon Alma

Fail2ban ile Otomatik IP Engelleme

Tespit ettiğiniz saldırı IP’lerini manuel olarak engelleyebilirsiniz, ama Fail2ban bu işi otomatize eder:

# Fail2ban nginx-http-auth jail'i aktif et
# /etc/fail2ban/jail.local
[nginx-http-auth]
enabled = true
filter = nginx-http-auth
port = http,https
logpath = /var/log/nginx/error.log
maxretry = 5
bantime = 3600
findtime = 600

# Nginx access log için özel jail
[nginx-req-limit]
enabled = true
filter = nginx-req-limit
port = http,https
logpath = /var/log/nginx/access.log
maxretry = 10
bantime = 7200
findtime = 60

iptables ile Manuel IP Engelleme

Hızlıca bir IP’yi engellemek için:

# Tek IP engelle
iptables -I INPUT -s 185.234.219.45 -j DROP

# IP aralığını engelle (CIDR)
iptables -I INPUT -s 185.234.0.0/16 -j DROP

# Mevcut engellenen IP'leri listele
iptables -L INPUT -n -v | grep DROP

# Nginx'te de direkt engelleme yapabilirsiniz
# /etc/nginx/conf.d/blocked.conf
deny 185.234.219.45;
deny 185.234.0.0/16;

Nginx’te Rate Limiting Uygulama

Saldırıları önlemenin en etkili yollarından biri rate limiting’dir:

# nginx.conf içinde rate limiting tanımla
http {
    # IP başına saniyede 10 istek izin ver
    limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
    limit_req_zone $binary_remote_addr zone=login:10m rate=1r/m;
    
    server {
        # Login endpoint'ine dakikada 1 istek
        location /wp-login.php {
            limit_req zone=login burst=5 nodelay;
            limit_req_status 429;
        }
        
        # API endpoint'lerine saniyede 10 istek
        location /api/ {
            limit_req zone=api burst=20 nodelay;
            limit_req_status 429;
        }
    }
}

Otomatik İzleme Script’i

Aşağıdaki script’i cron job olarak çalıştırarak şüpheli aktiviteleri e-posta ile bildirebilirsiniz:

#!/bin/bash
# /usr/local/bin/nginx-monitor.sh

LOG_FILE="/var/log/nginx/access.log"
THRESHOLD=1000  # Saatte 1000'den fazla istek şüpheli
ALERT_EMAIL="[email protected]"
CURRENT_HOUR=$(date +'%d/%b/%Y:%H')

# Bu saatte en çok istek yapan IP'leri al
SUSPICIOUS_IPS=$(grep "$CURRENT_HOUR" "$LOG_FILE" | 
    awk '{print $1}' | sort | uniq -c | sort -rn | 
    awk -v threshold="$THRESHOLD" '$1 > threshold {print $1, $2}')

if [ -n "$SUSPICIOUS_IPS" ]; then
    REPORT="Siipheli IP Aktivitesi Tespit Edildi!nn"
    REPORT+="Tarih: $(date)n"
    REPORT+="Esligi gecen IP'ler (${THRESHOLD} istek/saat):nn"
    REPORT+="$SUSPICIOUS_IPSnn"
    
    # 404 orani yuksek mi?
    TOTAL_404=$(grep "$CURRENT_HOUR" "$LOG_FILE" | awk '$9 == 404' | wc -l)
    REPORT+="Bu saatteki toplam 404 hatasi: $TOTAL_404n"
    
    echo -e "$REPORT" | mail -s "[ALARM] Nginx Saldiri Tespiti" "$ALERT_EMAIL"
    
    # Opsiyonel: Otomatik engelle
    # echo "$SUSPICIOUS_IPS" | awk '{print $2}' | while read ip; do
    #     iptables -I INPUT -s "$ip" -j DROP
    # done
fi

Bu script’i 5 dakikada bir çalıştırmak için crontab’a ekleyin:

# crontab -e
*/5 * * * * /usr/local/bin/nginx-monitor.sh

Log Rotation ve Uzun Vadeli Analiz

Büyük sitelerde access logları hızla büyür. Log rotation ve arşivleme stratejiniz olmalı:

# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
    daily
    missingok
    rotate 30
    compress
    delaycompress
    notifempty
    create 0640 www-data adm
    sharedscripts
    postrotate
        if [ -f /var/run/nginx.pid ]; then
            kill -USR1 `cat /var/run/nginx.pid`
        fi
    endscript
}

# Arşivlenmiş logları da analiz etmek için
zcat /var/log/nginx/access.log.2.gz | awk '{print $1}' | sort | uniq -c | sort -rn | head -20

# Tüm log dosyalarını birleştirerek analiz et
cat /var/log/nginx/access.log /var/log/nginx/access.log.1 <(zcat /var/log/nginx/access.log*.gz 2>/dev/null) | awk '{print $1}' | sort | uniq -c | sort -rn | head -30

Gerçek Dünya: Log Analizinde Karşılaşılan Senaryolar

Yıllar içinde karşılaştığım birkaç ilginç durumu paylaşmak istiyorum.

Senaryo 1: Yavaş DDoS Saldırısı Bir müşterinin sitesi yavaşlıyordu ama belirgin bir DDoS görünmüyordu. Logları dakika bazında analiz ettiğimde, 500 farklı IP adresinden her birinin dakikada 10-15 istek yaptığını gördüm. Toplamda dakikada 5000-7000 istek, sunucuyu bunaltmak için yeterliydi. Hepsinin aynı user-agent’ı kullandığını fark edince botnet olduğu anlaşıldı.

Senaryo 2: İçeriden Tehdit Bir şirketin loglarında mesai saatleri dışında (gece 2-4 arası) belirli bir iç IP’den büyük boyutlu dosya indirme istekleri geldiğini tespit ettik. Yanıt boyutları 50-200MB arasındaydı. Veri sızdırma girişimiydi.

Senaryo 3: Zero-Day Tarama Log’da bir anda /cgi-bin/, /struts/, /actuator/, /solr/ gibi path’lere çok sayıda istek gelmeye başladı. Bunlar bilinen güvenlik açıklarına ait endpoint’lerdi. Otomatik tarama yapan bir bot, yeni bir açığı exploit etmeye çalışıyordu.

Bu senaryoların hepsi dikkatli log analizi ile erkenden tespit edildi ve büyük sorunlar önlendi.

Sonuç

Nginx access log analizi, sistem güvenliğinin temel taşlarından biridir. Düzenli log incelemesi yapmak, saldırıları erken tespit etmenizi ve proaktif önlem almanızı sağlar. Bu yazıda anlattığımız araçlar ve teknikler, günlük sistem yönetimi rutininizin bir parçası olmalıdır.

Başlangıç için şunları yapmanızı öneririm: önce log formatınızın doğru ayarlandığından emin olun, ardından basit awk komutlarıyla düzenli kontroller yapın. GoAccess’i kurun ve günlük HTML raporları oluşturun. Fail2ban’ı brute force koruması için aktif edin ve en kritik endpoint’lerinize rate limiting ekleyin. İzleme script’ini cron’a ekleyip e-posta bildirimlerini aktif edin.

Log analizi bir kere yapıp geçilen bir iş değildir. Saldırganların taktikleri sürekli değişir, sizin de analiz yöntemlerinizin buna ayak uydurması gerekir. Loglarınız size her şeyi anlatır, yeter ki dinlemesini bilin.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir