From 626fc4ba2bbfe5e0cfd154b2c5976cbfc64dae77 Mon Sep 17 00:00:00 2001 From: Yasen Pramatarov Date: Fri, 6 Dec 2024 15:25:15 +0200 Subject: [PATCH] Adds rate limiter class --- app/classes/ratelimitrer.php | 91 ++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 app/classes/ratelimitrer.php diff --git a/app/classes/ratelimitrer.php b/app/classes/ratelimitrer.php new file mode 100644 index 0000000..f4916d5 --- /dev/null +++ b/app/classes/ratelimitrer.php @@ -0,0 +1,91 @@ +db = $database->getConnection(); + $this->createTableIfNotExists(); + } + + private function createTableIfNotExists() { + $sql = "CREATE TABLE IF NOT EXISTS {$this->tableName} ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + ip_address VARCHAR(45) NOT NULL, + username VARCHAR(255) NOT NULL, + attempted_at DATETIME DEFAULT CURRENT_TIMESTAMP, + INDEX idx_ip_username (ip_address, username) + )"; + + $this->db->exec($sql); + } + + public function attempt($username, $ipAddress) { + // Clean old attempts + $this->clearOldAttempts(); + + // Record this attempt + $sql = "INSERT INTO {$this->tableName} (ip_address, username) VALUES (:ip, :username)"; + $stmt = $this->db->prepare($sql); + $stmt->execute([ + ':ip' => $ipAddress, + ':username' => $username + ]); + + // Check if too many attempts + return !$this->tooManyAttempts($username, $ipAddress); + } + + public function tooManyAttempts($username, $ipAddress) { + $sql = "SELECT COUNT(*) as attempts + FROM {$this->tableName} + WHERE ip_address = :ip + AND username = :username + AND attempted_at > datetime('now', '-' || :minutes || ' minutes')"; + + $stmt = $this->db->prepare($sql); + $stmt->execute([ + ':ip' => $ipAddress, + ':username' => $username, + ':minutes' => $this->decayMinutes + ]); + + $result = $stmt->fetch(PDO::FETCH_ASSOC); + return $result['attempts'] >= $this->maxAttempts; + } + + public function clearOldAttempts() { + $sql = "DELETE FROM {$this->tableName} + WHERE attempted_at < datetime('now', '-' || :minutes || ' minutes')"; + + $stmt = $this->db->prepare($sql); + $stmt->execute([ + ':minutes' => $this->decayMinutes + ]); + } + + public function getRemainingAttempts($username, $ipAddress) { + $sql = "SELECT COUNT(*) as attempts + FROM {$this->tableName} + WHERE ip_address = :ip + AND username = :username + AND attempted_at > datetime('now', '-' || :minutes || ' minutes')"; + + $stmt = $this->db->prepare($sql); + $stmt->execute([ + ':ip' => $ipAddress, + ':username' => $username, + ':minutes' => $this->decayMinutes + ]); + + $result = $stmt->fetch(PDO::FETCH_ASSOC); + return max(0, $this->maxAttempts - $result['attempts']); + } + + public function getDecayMinutes() { + return $this->decayMinutes; + } +}