Enhances ratelimiter to include page requests, configurable
parent
00e2a38087
commit
beafdf29fb
|
@ -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']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue