Hash IP for API rate limit
diff mbox

Message ID 20180422093717.5751-1-bluewind@xinu.at
State New
Headers show

Commit Message

Florian Pritz April 22, 2018, 9:37 a.m. UTC
The hash uses the start of a fixed window as an HMAC key. Therefore the
windows stored in the DB also use this so that there can only be one
active window for each IP.

Signed-off-by: Florian Pritz <bluewind@xinu.at>
---
 schema/aur-schema.sql     |  2 +-
 upgrading/4.7.0.txt       |  2 +-
 web/lib/aurjson.class.php | 12 +++++++-----
 3 files changed, 9 insertions(+), 7 deletions(-)

Patch
diff mbox

diff --git a/schema/aur-schema.sql b/schema/aur-schema.sql
index 79de3f2..45f8993 100644
--- a/schema/aur-schema.sql
+++ b/schema/aur-schema.sql
@@ -403,7 +403,7 @@  CREATE TABLE AcceptedTerms (
 -- Rate limits for API
 --
 CREATE TABLE `ApiRateLimit` (
-  IP VARCHAR(45) NOT NULL,
+  IP VARCHAR(255) NOT NULL,
   Requests INT(11) NOT NULL,
   WindowStart BIGINT(20) NOT NULL,
   PRIMARY KEY (`ip`)
diff --git a/upgrading/4.7.0.txt b/upgrading/4.7.0.txt
index 820e454..fcdbb4d 100644
--- a/upgrading/4.7.0.txt
+++ b/upgrading/4.7.0.txt
@@ -2,7 +2,7 @@ 
 
 ---
 CREATE TABLE `ApiRateLimit` (
-  IP VARCHAR(45) NOT NULL,
+  IP VARCHAR(255) NOT NULL,
   Requests INT(11) NOT NULL,
   WindowStart BIGINT(20) NOT NULL,
   PRIMARY KEY (`ip`)
diff --git a/web/lib/aurjson.class.php b/web/lib/aurjson.class.php
index c51e9c2..168c6b7 100644
--- a/web/lib/aurjson.class.php
+++ b/web/lib/aurjson.class.php
@@ -153,6 +153,9 @@  private function check_ratelimit($ip) {
 		}
 
 		$window_length = config_get("ratelimit", "window_length");
+		$hmac_key = floor(time() / ($window_length));
+		$ip = hash_hmac("sha256", $ip, $hmac_key);
+
 		$this->update_ratelimit($ip);
 		$stmt = $this->dbh->prepare("
 			SELECT Requests FROM ApiRateLimit
@@ -181,14 +184,13 @@  private function check_ratelimit($ip) {
 	private function update_ratelimit($ip) {
 		$window_length = config_get("ratelimit", "window_length");
 		$db_backend = config_get("database", "backend");
-		$time = time();
+		$window_start = floor(time() / ($window_length));
 
 		// Clean up old windows
-		$deletion_time = $time - $window_length;
 		$stmt = $this->dbh->prepare("
 			DELETE FROM ApiRateLimit
 			WHERE WindowStart < :time");
-		$stmt->bindParam(":time", $deletion_time);
+		$stmt->bindParam(":time", $window_start);
 		$stmt->execute();
 
 		if ($db_backend == "mysql") {
@@ -198,7 +200,7 @@  private function update_ratelimit($ip) {
 				VALUES (:ip, 1, :window_start)
 				ON DUPLICATE KEY UPDATE Requests=Requests+1");
 			$stmt->bindParam(":ip", $ip);
-			$stmt->bindParam(":window_start", $time);
+			$stmt->bindParam(":window_start", $window_start);
 			$stmt->execute();
 		} elseif ($db_backend == "sqlite") {
 			$stmt = $this->dbh->prepare("
@@ -206,7 +208,7 @@  private function update_ratelimit($ip) {
 				(IP, Requests, WindowStart)
 				VALUES (:ip, 0, :window_start);");
 			$stmt->bindParam(":ip", $ip);
-			$stmt->bindParam(":window_start", $time);
+			$stmt->bindParam(":window_start", $window_start);
 			$stmt->execute();
 
 			$stmt = $this->dbh->prepare("