extend rate limiter

This commit is contained in:
O K
2026-04-05 21:20:54 +03:00
parent 811451bd1c
commit 961c1ed53d
5 changed files with 58 additions and 38 deletions

41
classes/RateLimiter.php Normal file
View File

@@ -0,0 +1,41 @@
<?php
class RateLimiter
{
/**
* @param string $ip The IP to check
* @param string $action A unique string for the action (e.g., '404', 'verify_attempt')
* @param int $max_requests
* @param int $window seconds
* @return bool True if allowed, false if limit exceeded
*/
public static function checkIsRateLimited($ip, $action, $max_requests, $window)
{
// 1. Check if the Cache module is available
if (!Module::isInstalled('dbmemorycache') || !Module::isEnabled('dbmemorycache')) {
return false; // Not limited if we can't track it
}
$cache = Module::getInstanceByName('dbmemorycache');
if (!$cache) {
return false;
}
// 2. Generate unique key for this IP + Action
$cacheKey = hash('sha256', 'bot_limit_' . $action . '_' . $ip);
// 3. Get current count
$currentCount = 0;
if ($cache->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);
}
}

View File

@@ -4,6 +4,7 @@ require_once dirname(__FILE__) . '/rules/HeadRequestRule.php';
require_once dirname(__FILE__) . '/rules/FilterTrapRule.php'; require_once dirname(__FILE__) . '/rules/FilterTrapRule.php';
require_once dirname(__FILE__) . '/rules/ScanBotsRule.php'; require_once dirname(__FILE__) . '/rules/ScanBotsRule.php';
require_once dirname(__FILE__) . '/rules/RateLimitRule.php'; require_once dirname(__FILE__) . '/rules/RateLimitRule.php';
require_once dirname(__FILE__) . '/RateLimiter.php';
class RuleManager class RuleManager

View File

@@ -69,7 +69,11 @@ class FilterTrapRule implements RuleInterface
die('Access Denied'); 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. // 5. If we are here: Heavy request + No Cookie + No Token.
// Redirect to the Trap (Verify Controller) // Redirect to the Trap (Verify Controller)

View File

@@ -21,48 +21,12 @@ class RateLimitRule implements RuleInterface
return true; return true;
} }
if (RateLimiter::checkIsRateLimited($ip, '404_spam', self::MAX_404_REQUESTS, self::TIME_WINDOW)) {
// 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
BotLogger::logBan($ip, '404_RATE_LIMIT_EXCEEDED'); BotLogger::logBan($ip, '404_RATE_LIMIT_EXCEEDED');
// Drop the connection instantly to save server resources
header('HTTP/1.1 429 Too Many Requests'); header('HTTP/1.1 429 Too Many Requests');
die('429 Too Many Requests - Stop Scanning'); 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; return true;
} }
} }

View File

@@ -1,11 +1,21 @@
<?php <?php
require_once dirname(__FILE__) . '/../../classes/BotLogger.php'; require_once dirname(__FILE__) . '/../../classes/BotLogger.php';
require_once dirname(__FILE__) . '/../../classes/RuleManager.php';
class BotLimiterVerifyModuleFrontController extends ModuleFrontController class BotLimiterVerifyModuleFrontController extends ModuleFrontController
{ {
public function initContent() public function initContent()
{ {
$ip = BotLogger::getRealIp();
// If they hit the verify page itself more than 5 times in 30 seconds
if (RateLimiter::checkIsRateLimited($ip, 'verify_page_load', 5, 30)) {
BotLogger::logBan($ip, 'VERIFY_PAGE_FLOOD');
header('HTTP/1.1 429 Too Many Requests');
die('Too many verification attempts.');
}
parent::initContent(); // This initializes the Standard PS Cookie parent::initContent(); // This initializes the Standard PS Cookie
$ip = BotLogger::getRealIp(); $ip = BotLogger::getRealIp();