From c3d6def2005d1acea3c4aa5f4e63116208596284 Mon Sep 17 00:00:00 2001 From: velz Date: Mon, 23 Feb 2026 18:55:38 +0530 Subject: [PATCH] FIX_API_RATE_LIMIT_COND --- app/Filters/AuthApiRateLimitFilter.php | 6 ++- app/Filters/JwtApiFilter.php | 6 ++- app/Helpers/utility_helper.php | 75 +++++++++++++++++++++++++- app/Libraries/RateLimiterService.php | 1 + 4 files changed, 83 insertions(+), 5 deletions(-) diff --git a/app/Filters/AuthApiRateLimitFilter.php b/app/Filters/AuthApiRateLimitFilter.php index 3dc0db21..944af97f 100644 --- a/app/Filters/AuthApiRateLimitFilter.php +++ b/app/Filters/AuthApiRateLimitFilter.php @@ -38,7 +38,8 @@ class AuthApiRateLimitFilter implements FilterInterface public function before(RequestInterface $request, $arguments = null) { - $fingerprint = generateFingerprint(); + $fingerprint = generateFingerprint(exclude_ua: true); + // 1. IP-level check $ipResult = $this->limiter->checkIp($fingerprint, 'authApi'); @@ -76,7 +77,8 @@ class AuthApiRateLimitFilter implements FilterInterface return; // 2xx/3xx = success; 429/403/451 already handled } - $fingerprint = $request->getGlobal('rateLimitFingerprint') ?? generateFingerprint(); + $fingerprint = $request->getGlobal('rateLimitFingerprint') ?? generateFingerprint(exclude_ua: true); + $identity = $request->getGlobal('rateLimitIdentity') ?? $this->resolveIdentity($request); diff --git a/app/Filters/JwtApiFilter.php b/app/Filters/JwtApiFilter.php index 2c887b68..a3c7173d 100644 --- a/app/Filters/JwtApiFilter.php +++ b/app/Filters/JwtApiFilter.php @@ -39,7 +39,8 @@ class JwtApiRateLimitFilter implements FilterInterface public function before(RequestInterface $request, $arguments = null) { - $fingerprint = generateFingerprint(); + $fingerprint = generateFingerprint(exclude_ua: true); + // 1. IP-level throttle + block check $ipResult = $this->limiter->checkIp($fingerprint, 'jwtApi'); @@ -83,7 +84,8 @@ class JwtApiRateLimitFilter implements FilterInterface return; } - $fingerprint = $request->getGlobal('rateLimitFingerprint') ?? generateFingerprint(); + $fingerprint = $request->getGlobal('rateLimitFingerprint') ?? generateFingerprint(exclude_ua: true); + $identity = $request->getGlobal('rateLimitIdentity') ?? $this->resolveIdentityFromJwt(); $this->limiter->recordIpFailure($fingerprint); diff --git a/app/Helpers/utility_helper.php b/app/Helpers/utility_helper.php index 7e6048ab..467f0338 100755 --- a/app/Helpers/utility_helper.php +++ b/app/Helpers/utility_helper.php @@ -1069,7 +1069,7 @@ function getRealClientIP() return $request->getIPAddress(); } -function generateFingerprint(): string +function generateFingerprint(bool $exclude_ua = false): string { $request = service('request'); @@ -1097,6 +1097,9 @@ function generateFingerprint(): string $ipGroup = 'unknown'; } + if($exclude_ua){ + return hash('sha256', $ipGroup); + } return hash('sha256', $ua . '|' . $ipGroup); } @@ -1203,4 +1206,74 @@ if (!function_exists('map_relationship')) { } +/** + * Extract identity from POST body or GET params. + * Looks for 'email' or 'mobile_number'. + */ + function resolveIdentity($request): ?string + { + // Try POST body first + $email = $request->getPost('email'); + // print_r($email);die; + $mobile = $request->getPost('mobile_number'); + + // Fallback to GET params + if (! $email && ! $mobile) { + $email = $request->getGet('email'); + $mobile = $request->getGet('mobile_number'); + } + // Fallback to JSON params + if (! $email && ! $mobile) { + $req_data = $request->getJSON(); + // print_r( $req_data); + + $mobile = $req_data->mobile_number ?? null; + // return trim($mobile_number); + + $email = $req_data->email ?? null; + + if (!$email) + { + $email = $req_data->email_id ?? null; + } + // return trim($email); + } + + if ($email) { + return strtolower(trim($email)); + } + + if ($mobile) { + return trim($mobile); + } + + return null; + } + + + function recordRateLimitFailure(string $context = 'authApi'): void + { + /** @var IncomingRequest $request */ + $request = \Config\Services::request(); + + $limiter = \Config\Services::limiter(); // or your custom limiter service + + $fingerprint = $request->getVar('rateLimitFingerprint') + ?? generateFingerprint(exclude_ua: true); + + + + $identity = $request->getVar('rateLimitIdentity') + ?? resolveIdentity($request); + + // Record IP-level failure + $limiter->recordIpFailure($fingerprint); + + // Record user-level failure + if (!empty($identity)) { + $limiter->recordUserFailure($identity, $context); + } + } + + diff --git a/app/Libraries/RateLimiterService.php b/app/Libraries/RateLimiterService.php index 80374101..90094907 100644 --- a/app/Libraries/RateLimiterService.php +++ b/app/Libraries/RateLimiterService.php @@ -111,6 +111,7 @@ class RateLimiterService 'level' => $level, 'blocked_at' => time(), 'fingerprint'=> $fingerprint, + 'ip' => getRealClientIP(), ]; // Duration 0 = store for 10 years (permanent until manual unblock)