fix(stream,bot-detector): prevent false-positive 403 bans and memory exhaustion on direct video streams

This commit is contained in:
Mustafa Yildiz
2026-08-21 13:55:19 +03:00
parent ffe6d93cf6
commit 19c8d496c7
2 changed files with 86 additions and 28 deletions
+40 -25
View File
@@ -61,20 +61,37 @@ class BotDetector
'postman', 'insomnia', 'httpie',
];
// Bu path'ler için sadece log tut, engelleme yapma
// Bu path'ler için sadece log tut, engelleme ve genel rate limit yapma
private const SKIP_PATHS = [
'/up', '/api/', '/sitemap',
'/up',
'/api/',
'/sitemap',
'/stream/',
'/vtt-proxy',
'/track/',
'/calls/',
'/episode/',
'/comments',
'/media/',
'/assets/',
'/build/',
'/vendor/',
];
public function handle(Request $request, Closure $next)
{
$ip = $request->ip();
$ua = strtolower($request->userAgent() ?? '');
$path = $request->path();
$path = '/' . ltrim($request->path(), '/');
// index.php önekini normalize et (/index.php/stream/seg -> /stream/seg)
if (str_starts_with($path, '/index.php/')) {
$path = '/' . substr($path, 11);
}
// Skip paths
foreach (self::SKIP_PATHS as $skip) {
if (str_starts_with('/' . $path, $skip)) {
if (str_starts_with($path, $skip)) {
return $next($request);
}
}
@@ -86,9 +103,9 @@ class BotDetector
}
}
// Manuel engelli IP kontrolü
// Manuel engelli IP kontrolü (Sadece yöneticinin manuel engellediği IP'ler)
if ($this->isBlockedIp($ip)) {
$this->logBot($ip, $request->userAgent(), '/' . $path, $request->method(), 'ip_blocked', 'blocked_ip');
$this->logBot($ip, $request->userAgent(), $path, $request->method(), 'ip_blocked', 'blocked_ip');
return response('Erişim engellendi.', 403);
}
@@ -96,14 +113,14 @@ class BotDetector
if (empty($ua)) {
$request->attributes->set('is_bot', true);
$request->attributes->set('bot_type', 'noua');
$this->logBot($ip, '', '/' . $path, $request->method(), 'allowed', 'no_ua');
$this->logBot($ip, '', $path, $request->method(), 'allowed', 'no_ua');
return $next($request);
}
// Kötü bot mu?
foreach (self::BAD_BOTS as $pattern) {
if (str_contains($ua, $pattern)) {
$this->logBot($ip, $request->userAgent(), '/' . $path, $request->method(), 'blocked', $pattern);
$this->logBot($ip, $request->userAgent(), $path, $request->method(), 'blocked', $pattern);
return response('', 403);
}
}
@@ -113,8 +130,8 @@ class BotDetector
if (str_contains($ua, $pattern)) {
$request->attributes->set('is_bot', true);
$request->attributes->set('bot_type', 'good');
// İyi botlar için çok agresif rate limit (dakikada 60)
if ($this->isRateLimited($ip, 60, 'good_bot')) {
// İyi botlar için rate limit (dakikada 120)
if ($this->isRateLimited($ip, 120, 'good_bot')) {
return response('', 429);
}
return $next($request);
@@ -126,28 +143,24 @@ class BotDetector
if (str_contains($ua, $pattern)) {
$request->attributes->set('is_bot', true);
$request->attributes->set('bot_type', 'generic');
if ($this->isRateLimited($ip, 10, 'generic')) {
$this->logBot($ip, $request->userAgent(), '/' . $path, $request->method(), 'rate_limited', $pattern);
// 30+ istek → otomatik engelle
if ($this->isRateLimited($ip, 20, 'generic')) {
$this->logBot($ip, $request->userAgent(), $path, $request->method(), 'rate_limited', $pattern);
// 50+ istek → otomatik engelle
$count = Cache::get("bot_count_{$ip}", 0);
if ($count > 30) {
$this->autoBlock($ip, 'Otomatik: dakikada 30+ generic bot isteği');
if ($count > 50) {
$this->autoBlock($ip, 'Otomatik: dakikada 50+ generic bot isteği');
}
return response('', 429);
}
$this->logBot($ip, $request->userAgent(), '/' . $path, $request->method(), 'allowed', $pattern);
$this->logBot($ip, $request->userAgent(), $path, $request->method(), 'allowed', $pattern);
return $next($request);
}
}
// Normal kullanıcı — genel rate limit (dakikada 120 istek)
if ($this->isRateLimited($ip, 120, 'human')) {
$this->logBot($ip, $request->userAgent(), '/' . $path, $request->method(), 'rate_limited', 'human_flood');
$count = Cache::get("bot_count_{$ip}", 0);
if ($count > 200) {
$this->autoBlock($ip, 'Otomatik: dakikada 200+ istek flood');
}
return response('', 429);
// Normal kullanıcı — genel sayfa istek limiti (dakikada 300 sayfa isteği)
if ($this->isRateLimited($ip, 300, 'human')) {
$this->logBot($ip, $request->userAgent(), $path, $request->method(), 'rate_limited', 'human_flood');
return response('Çok fazla istek gönderdiniz. Lütfen biraz bekleyin.', 429);
}
$request->attributes->set('is_bot', false);
@@ -156,10 +169,11 @@ class BotDetector
private function isBlockedIp(string $ip): bool
{
return Cache::remember("blocked_ip_{$ip}", 300, function () use ($ip) {
return Cache::remember("blocked_ip_{$ip}", 30, function () use ($ip) {
try {
return DB::table('blocked_ips')
->where('ip', $ip)
->where('auto_blocked', 0)
->where(function ($q) {
$q->whereNull('expires_at')->orWhere('expires_at', '>', now());
})
@@ -216,3 +230,4 @@ class BotDetector
} catch (\Exception) {}
}
}