Fixed Window Açığı
Bir SaaS API’sini korurken, rate limiting DDoS saldırıları ve brute-force scraping’e karşı ilk savunma hattınızdır. Çoğu framework’teki varsayılan yaklaşım Fixed Window algoritmasıdır (örneğin, dakikada 60 istek izni verilmesi).
Burada gizli bir mimari açığın bulunduğu nokta şudur: Kötü niyetli bir script, 11:59:59 AM’de 60 isteği gönderebilir ve 12:00:01 PM’de bir 60 istek daha gönderebilir. Çünkü “dakika penceresi” tam olarak 12:00:00’da sıfırlanır, sunucu 2 saniyede 120 isteği almış olur. Bu ani trafik, niyetlenen sınırları aşarak veritabanı bağlantı havuzunuzu aşırı yükleyip API’nizin çökmesine neden olur. Matematiksel olarak düzgün bir trafik akışını garanti altına almak için, Sliding Window algoritmasını uygulamanız gerekir.
Çözüm: Redis Sorted Sets (ZSET)
Sliding Window algoritması çok miktarda isteği sıfırlamaz. Bunun yerine, mevcut mikro saniyeden tam olarak 60 saniye öncesine bakar. Eğer dinamik, sürekli zaman diliminde 60’tan fazla istek varsa, isteği engeller.
Bunu, Redis Sorted Set (ZSET) kullanarak son derece performanslı bir şekilde inşa edebiliriz. Mevcut Unix zaman damgasını “skor” olarak kullanırız, bu sayede eski istekleri anında düşürür ve son istekleri atomik olarak sayarız.
Sliding Window Middleware’ini Tasarlamak
Bu sliding window kontrolünü doğrudan Redis C seviyesinde gerçekleştiren özel bir Laravel middleware yazalım; böylece istek veritabanımıza ulaşmadan önce kontrol edilmiş olur.
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redis;
use Symfony\Component\HttpFoundation\Response;
class SlidingWindowRateLimiter
{
public function handle(Request $request, Closure $next, int $limit = 60, int $windowSeconds = 60)
{
$ip = $request->ip();
$redisKey = "rate_limit:sliding:{$ip}";
$now = microtime(true);
$windowStart = $now - $windowSeconds;
// 1. 60 saniyelik pencere dışındaki tüm istek zaman damgalarını kaldır
Redis::zremrangebyscore($redisKey, '-inf', $windowStart);
// 2. Mevcut döngüsel penceredeki istek sayısını hesapla
$currentRequests = Redis::zcard($redisKey);
if ($currentRequests >= $limit) {
return response()->json([
'error' => 'Too Many Requests.',
'retry_after' => $windowSeconds
], Response::HTTP_TOO_MANY_REQUESTS);
}
// 3. Mevcut isteğin zaman damgasını Sorted Set'e ekle
// Hem skor (sıralama için) hem de üye değeri olarak $now kullanıyoruz
Redis::zadd($redisKey, $now, $now);
// 4. Kullanıcı çıktığında bellek sızıntısını önlemek için anahtara bir son tarih ayarla
Redis::expire($redisKey, $windowSeconds);
return $next($request);
}
}
Mühendislik ROI’si
Fixed Window’dan bir Redis Sliding Window’a geçerek, “sınır patlaması” açığını tamamen ortadan kaldırıyorsunuz. API trafiğiniz, mikro saniyelik DDoS zirvelerine karşı arka uç altyapınızı koruyarak, düzgün ve sürekli bir akışa zorlanmaktadır ve gecikmenizi son derece düşük tutmaktadır (Redis’te milisaniye altı çalışma süreleri).
Kaynak: Orijinal Makale


