diff --git a/classes/RateLimiter.php b/classes/RateLimiter.php new file mode 100644 index 0000000..147284f --- /dev/null +++ b/classes/RateLimiter.php @@ -0,0 +1,41 @@ +existsValue($cacheKey)) { + $currentCount = (int) $cache->getValue($cacheKey); + } + + $currentCount++; + + // 4. Save back with the window (Resets timer on every hit = Sliding Window) + $cache->setValue($cacheKey, $currentCount, $window); + + // 5. Return status + return ($currentCount > $max_requests); + } +} \ No newline at end of file diff --git a/classes/RuleManager.php b/classes/RuleManager.php index c154037..89e2f38 100644 --- a/classes/RuleManager.php +++ b/classes/RuleManager.php @@ -4,6 +4,7 @@ require_once dirname(__FILE__) . '/rules/HeadRequestRule.php'; require_once dirname(__FILE__) . '/rules/FilterTrapRule.php'; require_once dirname(__FILE__) . '/rules/ScanBotsRule.php'; require_once dirname(__FILE__) . '/rules/RateLimitRule.php'; +require_once dirname(__FILE__) . '/RateLimiter.php'; class RuleManager diff --git a/classes/rules/FilterTrapRule.php b/classes/rules/FilterTrapRule.php index f9b18c2..de53d62 100644 --- a/classes/rules/FilterTrapRule.php +++ b/classes/rules/FilterTrapRule.php @@ -69,7 +69,11 @@ class FilterTrapRule implements RuleInterface die('Access Denied'); } } - + if (RateLimiter::checkIsRateLimited($ip, 'filter_trap', 10, 60)) { + BotLogger::logBan($ip, 'FILTER_TRAP_SPAM'); + header('HTTP/1.1 429 Too Many Requests'); + die('Too many filter attempts.'); + } // 5. If we are here: Heavy request + No Cookie + No Token. // Redirect to the Trap (Verify Controller) diff --git a/classes/rules/RateLimitRule.php b/classes/rules/RateLimitRule.php index bb16cc7..5a5d40c 100644 --- a/classes/rules/RateLimitRule.php +++ b/classes/rules/RateLimitRule.php @@ -21,48 +21,12 @@ class RateLimitRule implements RuleInterface return true; } - - - - // 2. Safely check if the Cache module is installed and enabled - if (!Module::isInstalled('dbmemorycache') || !Module::isEnabled('dbmemorycache')) { - return true; // Pass gracefully if the module is missing or disabled - } - - /** @var DbMemoryCache $cache */ - $cache = Module::getInstanceByName('dbmemorycache'); - if (!$cache) { - return true; - } - - // 3. Generate a unique hash for this specific IP's 404 traffic - $cacheKey = hash('sha256', '404_spam_limiter_' . $ip); - - // 4. Lookup their current 404 hit count in the memory table - $currentCount = 0; - if ($cache->existsValue($cacheKey)) { - $currentCount = (int) $cache->getValue($cacheKey); - } - - // 5. Increment their strike counter - $currentCount++; - - // 6. Have they triggered too many 404s? - if ($currentCount > self::MAX_404_REQUESTS) { - - // Log it so Fail2ban can block them at the server firewall level + if (RateLimiter::checkIsRateLimited($ip, '404_spam', self::MAX_404_REQUESTS, self::TIME_WINDOW)) { BotLogger::logBan($ip, '404_RATE_LIMIT_EXCEEDED'); - - // Drop the connection instantly to save server resources header('HTTP/1.1 429 Too Many Requests'); die('429 Too Many Requests - Stop Scanning'); } - // 7. Save the new strike count back to the cache - // Note: Because we overwrite it here, this creates a "Sliding Window". - // If a bot keeps spamming, the 300s timer resets every time, keeping them trapped! - $cache->setValue($cacheKey, $currentCount, self::TIME_WINDOW); - return true; } } diff --git a/controllers/front/verify.php b/controllers/front/verify.php index 4262af5..ac2e627 100644 --- a/controllers/front/verify.php +++ b/controllers/front/verify.php @@ -1,11 +1,21 @@