first commit
This commit is contained in:
82
backend/core/Auth/RateLimiter.php
Normal file
82
backend/core/Auth/RateLimiter.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
// ============================================================
|
||||
// core/Auth/RateLimiter.php
|
||||
// Sliding Window Rate Limiting باستخدام Redis
|
||||
// ============================================================
|
||||
|
||||
class RateLimiter
|
||||
{
|
||||
private ?Redis $redis;
|
||||
|
||||
// حدود مختلفة لكل نوع endpoint
|
||||
private const LIMITS = [
|
||||
'login' => ['requests' => 5, 'window' => 60], // 5 محاولات / دقيقة
|
||||
'otp' => ['requests' => 3, 'window' => 300], // 3 محاولات / 5 دقائق
|
||||
'register' => ['requests' => 3, 'window' => 3600], // 3 محاولات / ساعة
|
||||
'api' => ['requests' => 120, 'window' => 60], // 120 طلب / دقيقة
|
||||
'ride' => ['requests' => 30, 'window' => 60], // 30 طلب / دقيقة
|
||||
'upload' => ['requests' => 10, 'window' => 300], // 10 رفع / 5 دقائق
|
||||
];
|
||||
|
||||
public function __construct(?Redis $redis)
|
||||
{
|
||||
$this->redis = $redis;
|
||||
}
|
||||
|
||||
// ── فحص الحد ─────────────────────────────────────────────
|
||||
// $identifier: IP:userId أو IP فقط
|
||||
// $type: login | otp | api | ride | upload
|
||||
public function check(string $identifier, string $type = 'api'): bool
|
||||
{
|
||||
if (!$this->redis) {
|
||||
return true; // بدون Redis نمرر (fallback)
|
||||
}
|
||||
|
||||
$limit = self::LIMITS[$type] ?? self::LIMITS['api'];
|
||||
$window = $limit['window'];
|
||||
$max = $limit['requests'];
|
||||
|
||||
$key = "rate:{$type}:{$identifier}";
|
||||
$current = $this->redis->incr($key);
|
||||
|
||||
if ($current === 1) {
|
||||
$this->redis->expire($key, $window);
|
||||
}
|
||||
|
||||
return $current <= $max;
|
||||
}
|
||||
|
||||
// ── تطبيق الحد وإيقاف الطلب إن تجاوز ─────────────────────
|
||||
public function enforce(string $identifier, string $type = 'api'): void
|
||||
{
|
||||
if (!$this->check($identifier, $type)) {
|
||||
$limit = self::LIMITS[$type] ?? self::LIMITS['api'];
|
||||
$window = $limit['window'];
|
||||
|
||||
error_log("[RATE_LIMIT] Blocked: $identifier | type: $type");
|
||||
|
||||
http_response_code(429);
|
||||
header("Retry-After: $window");
|
||||
echo json_encode([
|
||||
'error' => 'Too many requests. Please slow down.',
|
||||
'retry_after' => $window,
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// ── بناء معرّف المستخدم ────────────────────────────────────
|
||||
public static function identifier(?string $userId = null): string
|
||||
{
|
||||
$ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
|
||||
return $userId ? "{$ip}:{$userId}" : $ip;
|
||||
}
|
||||
|
||||
// ── إعادة تعيين عداد (مثلاً بعد تسجيل دخول ناجح) ───────────
|
||||
public function reset(string $identifier, string $type = 'login'): void
|
||||
{
|
||||
if ($this->redis) {
|
||||
$this->redis->del("rate:{$type}:{$identifier}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user