Rate Limiting ve DDoS Savunma: Kavramlar ve Katmanlar
Rate limiting, bir istemcinin belirli bir zaman penceresinde yapabileceği istek sayısını sınırlamaktır. DDoS savunması ise çok daha geniş bir problemdir: dağıtık kaynaklardan gelen, çoğu zaman meşru trafiğe benzeyen aşırı yükü uygulamayı çökertmeden karşılamak veya elemek. Bu iki konu sık karıştırılır çünkü rate limiting, DDoS savunmasının yalnızca bir katmanıdır — ve tek başına yeterli değildir.
Bu rehberde katmanlı bir savunma yaklaşımı izleyeceğiz: ağ/edge katmanı, uygulama katmanı ve veri katmanı. Kod örnekleri için dil ve sürüm bağımsız kalacağım; her örnekte hangi ortamda çalıştığını açıkça belirteceğim.
Önkoşullar
- HTTP semantiği ve durum kodları (özellikle
429,503,Retry-After) - Ters proxy veya load balancer kavramları (Nginx, Envoy, bulut LB)
- Paylaşımlı durum için Redis veya benzeri bir key-value store
- Temel ağ bilgisi: TCP/TLS el sıkışması, bağlantı havuzu
Rate Limiting Algoritmaları
Aşağıdaki algoritmalar birbirinin yerine geçmez; farklı trafik profilleri için farklı sonuçlar üretir.
Sabit Pencere (Fixed Window)
Belirli bir zaman aralığında (örn. her dakika başında) bir sayaç tutulur. Basittir ama pencere sınırında ani patlamalara izin verir: bir istemci dakikanın son saniyesinde limitini doldurup, bir sonraki dakikanın ilk saniyesinde tekrar doldurabilir.
Kayan Pencere (Sliding Window)
İsteğin zamanına göre geriye dönük bir pencere hesaplanır. Sabit pencereden daha adil sonuç verir, ancak her istek için zaman damgası saklamak veya ağırlıklı sayaç tutmak gerekir.
Token Bucket
Bir kova sabit hızda token ile dolar; her istek bir token tüketir. Token yoksa istek reddedilir. Ortalama hızı sınırlarken kısa süreli patlamalara (burst) izin vermek istediğinizde uygundur. burst parametresi, kovanın kapasitesini belirler.
Leaky Bucket
Kuyruk benzeri çalışır: istekler sabit hızda işlenir, kuyruk dolduğunda yenileri reddedilir. Çıkış hızı sabittir; girişteki dalgalanmaları yumuşatır.
Algoritma seçimi şu sorulara bağlıdır: Patlamalara izin verilecek mi? Adalet ne kadar önemli? Dağıtık ortamda paylaşımlı sayaç maliyeti kabul edilebilir mi?
Katman 1: Edge ve Ağ Seviyesi
Uygulamaya ulaşan trafiği mümkün olduğunca erken elemek, kaynak maliyetini düşürür.
Ters Proxy'de Temel Sınırlama
Nginx'in limit_req modülü, IP başına istek hızını sınırlar. Aşağıdaki örnek, nginx.conf içinde http bloğuna eklenir:
http {
limit_req_zone $binary_remote_addr zone=perip:10m rate=10r/s;
server {
location /api/ {
limit_req zone=perip burst=20 nodelay;
limit_req_status 429;
proxy_pass http://backend;
}
}
}
Beklenen davranış: aynı IP'den saniyede 10 isteğe kadar anında geçer; burst=20 ile kısa patlamalar kuyruğa alınır; nodelay kuyruğu beklemeden iletir; limit aşılırsa 429 döner. 10m alanı yaklaşık 160.000 IP durumu tutabilir — bu sayı Nginx sürümüne ve anahtar uzunluğuna göre değişebilir, kesin değer için kendi ortamınızda ölçün.
$binary_remote_addr kullanmak, $remote_addr'e göre daha az bellek harcar.
Yaygın Hata: Proxy Arkasında Gerçek IP
Uygulama bir LB arkasındaysa $binary_remote_addr LB'nin IP'sini görür ve tüm istemciler tek anahtar altında toplanır. X-Forwarded-For header'ı güvenilir proxy'lerden geldiğinde değerlendirilmeli, aksi halde istemci bu header'ı taklit ederek limitleri atlatır. Nginx'te real_ip modülü ve set_real_ip_from yönergeleri bu amaçla kullanılır; güvenilir kaynak ağları açıkça listelenmelidir.
Katman 2: Uygulama Seviyesi
Edge katmanı kaba filtre uygular; ince taneli kurallar uygulama içinde gerekir: kullanıcı başına, endpoint başına, API anahtarı başına farklı limitler.
Redis ile Token Bucket
Aşağıdaki örnek, Redis'te token bucket mantığını Lua script ile atomik hale getirir. Redis 7.x üzerinde EVAL ile çalışır. Script, kova durumunu bir hash'te tutar.
-- KEYS[1]: bucket anahtarı
-- ARGV[1]: kapasite (maksimum token)
-- ARGV[2]: saniyedeki dolum hızı
-- ARGV[3]: şu anki zaman (saniye, ondalıklı)
-- ARGV[4]: istenen token sayısı
local capacity = tonumber(ARGV[1])
local rate = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local requested = tonumber(ARGV[4])
local data = redis.call("HMGET", KEYS[1], "tokens", "ts")
local tokens = tonumber(data[1])
local ts = tonumber(data[2])
if tokens == nil then
tokens = capacity
ts = now
end
local delta = math.max(0, now - ts)
tokens = math.min(capacity, tokens + delta * rate)
local allowed = tokens >= requested
if allowed then
tokens = tokens - requested
end
redis.call("HMSET", KEYS[1], "tokens", tokens, "ts", now)
redis.call("EXPIRE", KEYS[1], math.ceil(capacity / rate) + 1)
if allowed then
return 1
else
return 0
end
Beklenen davranış: script atomik çalıştığı için yarış koşulu oluşmaz; kova dolum hızı rate, kapasite capacity ile sınırlanır. EXPIRE, kullanılmayan kovaların Redis'te birikmesini engeller.
Çağrı tarafında (örneğin Node.js'te ioredis veya Python'da redis-py) script EVALSHA ile önbelleğe alınarak çalıştırılmalıdır; her istekte script metnini göndermek gereksiz ağ yükü yaratır.
Yaygın Hata: Atomiklik
Sayaç okuma ve yazma işlemini ayrı komutlarla yapmak, eşzamanlı istekler altında limitin aşılmasına yol açar. INCR + EXPIRE ikilisi bile, EXPIRE çağrılmadan önce süreç çökerse anahtarın süresiz kalmasına neden olabilir. Lua script veya MULTI/EXEC ile atomik yapın.
Yaygın Hata: Sayaç Anahtarının Süresi
Sabit pencere uygularken INCR sonrası yalnızca ilk istekte EXPIRE çağırmak yaygın bir kalıptır. Ancak INCR ile EXPIRE arasında bir çökme olursa anahtar kalıcı hale gelir ve istemci kalıcı olarak bloke kalır. Redis 7.x'te SET üzerine kurulu EXPIRE NX gibi seçenekler veya Lua script bu riski ortadan kaldırır.
Katman 3: Kimlik Doğrulama ve Kota
Rate limiting yalnızca IP'ye bağlanırsa, NAT arkasındaki kullanıcılar birbirini etkiler ve saldırgan IP değiştirerek limiti atlar. Kimlik doğrulanmış isteklerde anahtar olarak kullanıcı ID'si veya API anahtarı kullanmak daha isabetlidir. Anonim istekler için IP, oturum çerezi veya cihaz parmak izi gibi sinyaller birleştirilebilir.
Kota (quota) ile rate limit farklıdır: kota, uzun vadeli bir toplamdır (örn. aylık 1 milyon istek), rate limit ise kısa vadeli hızdır. İkisi birlikte uygulanır.
DDoS Savunmasında Katmanlı Yaklaşım
DDoS savunması tek bir teknik değil, bir dizi savunma hattıdır. Sıralı olarak:
- Kenar (edge) filtreleme: CDN veya anycast ağ üzerinden trafiği emmek ve kötü niyetli kaynakları elemek. Sağlayıcı tarafındaki yapılandırma bu rehberin kapsamı dışındadır; ancak uygulama tarafında
X-Forwarded-Forve gerçek istemci IP'sinin doğru yorumlanması bu katmanla uyumlu olmalıdır. - Bağlantı seviyesi kontrolleri: SYN flood gibi saldırılara karşı işletim sistemi ve LB seviyesinde koruma. Uygulama kodunda çözülmez.
- İstek seviyesi rate limiting: Yukarıda anlatılan katmanlar.
- Davranışsal analiz: Aynı uç noktaya anormal sıklıkta erişen, yavaş istek gönderen (slowloris) veya tekrarlayan desenlerle gelen trafiği işaretleme.
- Zarif bozulma (graceful degradation): Yük altında pahalı uç noktaları devre dışı bırakmak, önbelleğe alınmış yanıtları sunmak, kuyruk kullanmak.
Yaygın Hata: Yalnızca Rate Limit'e Güvenmek
Dağıtık bir saldırıda binlerce farklı IP, her biri limitin altında istek gönderdiğinde IP başına rate limiting işe yaramaz. Bu durumda davranışsal sinyaller (istek deseni, user-agent tutarlılığı, TLS parmak izi) ve global limitler gerekir.
İstemci Tarafı: Doğru Davranış
Sunucu 429 döndüğünde istemcinin ne yapacağı da savunmanın parçasıdır. Standart yaklaşım:
Retry-Afterheader'ı varsa bu süre kadar beklemek.- Üstel geri çekilme (exponential backoff) ve jitter uygulamak; sabit aralıklarla tekrar denemek, saldırı etkisini artırır.
429'u geçici bir hata olarak ele alıp yeniden denemek; ancak sonsuz döngüye girmemek.
Sunucu tarafında Retry-After header'ını saniye cinsinden veya HTTP tarihi olarak döndürmek, istemcilerin doğru davranmasını kolaylaştırır.
Gözlemlenebilirlik
Rate limiting ve DDoS savunması, ölçülmediği sürece ayarlanamaz. En azından şu metrikler toplanmalıdır:
- Reddedilen istek sayısı, limit tipi ve anahtar bazında
429ve503oranları- Limiti en çok aşan anahtarlar
- Ortalama ve p99 gecikme; yük altındaki değişim
Bu metrikler olmadan limit değerleri tahminle belirlenir ve ya çok gevşek (koruma sağlamaz) ya da çok sıkı (meşru kullanıcıyı engeller) olur.
Özet
Rate limiting bir kontrol mekanizmasıdır; DDoS savunması ise katmanlı bir mimari gerektirir. Edge'de kaba filtreleme, uygulamada atomik ve kimlik tabanlı limitler, istemcide doğru geri çekilme davranışı ve her katmanda gözlemlenebilirlik birlikte çalıştığında anlamlı bir koruma oluşur. Algoritma seçimi trafik profiline bağlıdır; atomiklik ve gerçek istemci kimliği ise neredeyse her senaryoda kritik iki ayrıntıdır.