Enhances ratelimiter to include page requests, configurable

main
Yasen Pramatarov 2025-02-17 15:04:50 +02:00
parent 00e2a38087
commit beafdf29fb
1 changed files with 138 additions and 1 deletions

View File

@ -8,8 +8,19 @@ class RateLimiter {
public $autoBlacklistThreshold = 10; // Attempts before auto-blacklist public $autoBlacklistThreshold = 10; // Attempts before auto-blacklist
public $autoBlacklistDuration = 24; // Hours to blacklist for public $autoBlacklistDuration = 24; // Hours to blacklist for
public $authRatelimitTable = 'login_attempts'; public $authRatelimitTable = 'login_attempts';
public $pagesRatelimitTable = 'pages_rate_limits';
public $whitelistTable = 'ip_whitelist'; public $whitelistTable = 'ip_whitelist';
public $blacklistTable = 'ip_blacklist'; public $blacklistTable = 'ip_blacklist';
private $pageLimits = [
// Default rate limits per minute
'default' => 60,
'admin' => 120,
'message' => 20,
'contact' => 30,
'call' => 30,
'register' => 5,
'config' => 10
];
public function __construct($database) { public function __construct($database) {
$this->db = $database->getConnection(); $this->db = $database->getConnection();
@ -19,7 +30,7 @@ class RateLimiter {
// Database preparation // Database preparation
private function createTablesIfNotExist() { private function createTablesIfNotExist() {
// Login attempts table // Authentication attempts table
$sql = "CREATE TABLE IF NOT EXISTS {$this->authRatelimitTable} ( $sql = "CREATE TABLE IF NOT EXISTS {$this->authRatelimitTable} (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
ip_address TEXT NOT NULL UNIQUE, ip_address TEXT NOT NULL UNIQUE,
@ -28,6 +39,15 @@ class RateLimiter {
)"; )";
$this->db->exec($sql); $this->db->exec($sql);
// Pages rate limits table
$sql = "CREATE TABLE IF NOT EXISTS {$this->pagesRatelimitTable} (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ip_address TEXT NOT NULL,
endpoint TEXT NOT NULL,
request_time DATETIME DEFAULT CURRENT_TIMESTAMP
)";
$this->db->exec($sql);
// IP whitelist table // IP whitelist table
$sql = "CREATE TABLE IF NOT EXISTS {$this->whitelistTable} ( $sql = "CREATE TABLE IF NOT EXISTS {$this->whitelistTable} (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
@ -454,4 +474,121 @@ class RateLimiter {
public function getDecayMinutes() { public function getDecayMinutes() {
return $this->decayMinutes; return $this->decayMinutes;
} }
/**
* Check if a page request is allowed
*/
public function isPageRequestAllowed($ipAddress, $endpoint, $userId = null) {
// First check if IP is blacklisted
if ($this->isIpBlacklisted($ipAddress)) {
return false;
}
// Then check if IP is whitelisted
if ($this->isIpWhitelisted($ipAddress)) {
return true;
}
// Clean old requests
$this->cleanOldPageRequests();
// Get limit based on endpoint type and user role
$limit = $this->getPageLimitForEndpoint($endpoint, $userId);
// Count recent requests
$sql = "SELECT COUNT(*) as request_count
FROM {$this->pagesRatelimitTable}
WHERE ip_address = :ip
AND endpoint = :endpoint
AND request_time > DATETIME('now', '-1 minute')";
$stmt = $this->db->prepare($sql);
$stmt->execute([
':ip' => $ipAddress,
':endpoint' => $endpoint
]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
return $result['request_count'] < $limit;
}
/**
* Record a page request
*/
public function recordPageRequest($ipAddress, $endpoint) {
$sql = "INSERT INTO {$this->pagesRatelimitTable} (ip_address, endpoint)
VALUES (:ip, :endpoint)";
$stmt = $this->db->prepare($sql);
return $stmt->execute([
':ip' => $ipAddress,
':endpoint' => $endpoint
]);
}
/**
* Clean old page requests
*/
private function cleanOldPageRequests() {
$sql = "DELETE FROM {$this->pagesRatelimitTable}
WHERE request_time < DATETIME('now', '-1 minute')";
$stmt = $this->db->prepare($sql);
$stmt->execute();
}
/**
* Get page rate limit for endpoint
*/
private function getPageLimitForEndpoint($endpoint, $userId = null) {
// Admin users get higher limits
if ($userId) {
$userObj = new User($this->db);
if ($userObj->hasRight($userId, 'admin')) {
return $this->pageLimits['admin'];
}
}
// Get endpoint type from the endpoint path
$endpointType = $this->getEndpointType($endpoint);
// Return specific limit if exists, otherwise default
return isset($this->pageLimits[$endpointType])
? $this->pageLimits[$endpointType]
: $this->pageLimits['default'];
}
/**
* Get endpoint type from path
*/
private function getEndpointType($endpoint) {
if (strpos($endpoint, 'message') !== false) return 'message';
if (strpos($endpoint, 'contact') !== false) return 'contact';
if (strpos($endpoint, 'call') !== false) return 'call';
if (strpos($endpoint, 'register') !== false) return 'register';
if (strpos($endpoint, 'config') !== false) return 'config';
return 'default';
}
/**
* Get remaining page requests
*/
public function getRemainingPageRequests($ipAddress, $endpoint, $userId = null) {
$limit = $this->getPageLimitForEndpoint($endpoint, $userId);
$sql = "SELECT COUNT(*) as request_count
FROM {$this->pagesRatelimitTable}
WHERE ip_address = :ip
AND endpoint = :endpoint
AND request_time > DATETIME('now', '-1 minute')";
$stmt = $this->db->prepare($sql);
$stmt->execute([
':ip' => $ipAddress,
':endpoint' => $endpoint
]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
return max(0, $limit - $result['request_count']);
}
} }