Files
botlimiter/classes/rules/RateLimitRule.php
2026-03-22 09:23:41 +02:00

69 lines
2.3 KiB
PHP

<?php
class RateLimitRule implements RuleInterface
{
const MAX_404_REQUESTS = 20; // Allow a maximum of 20 dead links...
const TIME_WINDOW = 300; // ...within 300 seconds (5 minutes)
public function execute()
{
$ip = BotLogger::getRealIp();
if (BotLogger::isWhitelisted($ip)) {
return true;
}
$context = Context::getContext();
// 1. Instantly skip if this is NOT a 404 error page.
// During the hookActionFrontControllerInitBefore hook, PrestaShop has already
// resolved the route. If it failed, the controller is set to PageNotFoundController.
if (!($context->controller instanceof PageNotFoundController)) {
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
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;
}
}