تخطَّ إلى المحتوى

🔐 شرح أمن API وقواعد البيانات

تحديد معدل الطلبات (Rate Limiting)

الدرس 7 من 25· ⏱ 4 دقائق قراءة

ما هو Rate Limiting؟

تحديد معدل الطلبات (Rate Limiting) هو تقنية تحد من عدد الطلبات التي يمكن للعميل إرسالها خلال فترة زمنية معينة. يمنع إساءة الاستخدام ويحمي الموارد.

لماذا نحتاج Rate Limiting؟

1. حماية من Brute Force: منع تخمين كلمات المرور
2. حماية من DDoS: منع إغراق الخادم بالطلبات
3. توزيع عادل للموارد: منع مستخدم واحد من استهلاك كل الموارد
4. تقليل التكاليف: حماية البنية التحتية من الاستخدام المفرط
5. الامتثال: بعض APIs تفرض حدودا لعملائها

تنفيذ Rate Limiting مع Express

أساسي - in-memory

const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 دقيقة
  max: 100, // حد أقصى 100 طلب لكل نافذة
  standardHeaders: true, // إرجاع headers قياسية
  legacyHeaders: false,  // إيقاف headers القديمة
  message: {
    error: 'طلبات كثيرة جدا',
    message: 'حاول مرة أخرى بعد 15 دقيقة'
  }
});

// تطبيق على كل الطلبات
app.use(limiter);

// أو على مسار محدد
app.use('/api/login', limiter);

لكل مستخدم (مع MongoDB)

const rateLimit = require('express-rate-limit');
const MongoStore = require('rate-limit-mongo');

// متجر MongoDB لتخزين عداد الطلبات
const authLimiter = rateLimit({
  windowMs: 60 * 60 * 1000, // ساعة
  max: 5, // 5 محاولات فاشلة
  store: new MongoStore({
    uri: process.env.MONGO_URI,
    collectionName: 'rateLimits',
    expireTimeMs: 60 * 60 * 1000
  }),
  skipSuccessfulRequests: true, // نحتاج فقط المحاولات الفاشلة
  keyGenerator: (req) => req.ip, // مفتاح فريد لكل IP
  handler: (req, res) => {
    res.status(429).json({
      error: 'محاولات كثيرة. حسابك مقفل مؤقتا.',
      retryAfter: Math.ceil(req.rateLimit.resetTime / 1000)
    });
  }
});

app.post('/api/login', authLimiter, loginHandler);

مع Redis (للإنتاج)

const rateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis');
const Redis = require('ioredis');

const redis = new Redis({
  host: process.env.REDIS_HOST,
  port: 6379,
  password: process.env.REDIS_PASSWORD
});

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 100,
  store: new RedisStore({
    sendCommand: (...args) => redis.call(...args),
    prefix: 'rl:' // بادئة لمفاتيح Redis
  })
});

X-RateLimit Headers

X-RateLimit-Limit: 100        # الحد الأقصى للطلبات
X-RateLimit-Remaining: 87     # الطلبات المتبقية
X-RateLimit-Reset: 1680000900 # وقت إعادة التعيين (Unix timestamp)
Retry-After: 873              # ثوانٍ حتى يمكن إعادة المحاولة
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 100,
  standardHeaders: true, // ✅ يتوافق مع RFC
  legacyHeaders: false,  // ❌ يلغي X-RateLimit-* القديمة
});

استراتيجيات متقدمة

Sliding Window (نافذة منزلقة)

const limiter = rateLimit({
  windowMs: 60 * 1000, // دقيقة
  max: 10,
  // النافذة المنزلقة تحسب الطلبات بناء على الطابع الزمني
  // بدلا من نافذة ثابتة تبدأ من أول طلب
  standardHeaders: true
});

Per-User vs Per-IP

// لكل مستخدم (بعد المصادقة)
app.use('/api/posts', (req, res, next) => {
  if (req.user) {
    req.rateLimitKey = req.user.id;
  }
  next();
}, rateLimit({
  windowMs: 60 * 1000,
  max: 30,
  keyGenerator: (req) => req.rateLimitKey || req.ip
}));

Rate Limiting متعدد الطبقات

// طبقة 1: حد عام
const globalLimiter = rateLimit({ windowMs: 60 * 1000, max: 1000 });
app.use(globalLimiter);

// طبقة 2: حد لمحاولات تسجيل الدخول
const loginLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 5 });
app.post('/api/login', loginLimiter, loginHandler);

// طبقة 3: حد للمصادقة الثنائية (أكثر تشددا)
const mfaLimiter = rateLimit({ windowMs: 60 * 60 * 1000, max: 3 });
app.post('/api/verify-mfa', mfaLimiter, mfaHandler);

ممارسات أفضل

// Rate Limiter كامل مع أفضل الممارسات
const createRateLimiter = (options = {}) => {
  return rateLimit({
    windowMs: options.windowMs || 15 * 60 * 1000,
    max: options.max || 100,
    standardHeaders: true,
    legacyHeaders: false,
    message: {
      error: 'طلبات كثيرة جدا',
      retryAfter: null
    },
    handler: (req, res, next, options) => {
      res.status(429).json({
        error: 'طلبات كثيرة جدا',
        retryAfter: Math.ceil(req.rateLimit.resetTime / 1000) || null,
        limit: req.rateLimit.limit,
        remaining: req.rateLimit.remaining
      });
    },
    ...options
  });
};

// الاستخدام
const strictLimiter = createRateLimiter({
  windowMs: 60 * 1000,
  max: 10,
  message: { error: 'تجاوزت الحد المسموح، انتظر دقيقة' }
});

⚠️ Rate Limiting ليس بديلا عن المصادقة الجيدة. استخدمه كطبقة حماية إضافية وليس كحل وحيد.

🎯 العودة إلى مقدمة في أمن التطبيقات

شرح تحديد معدل الطلبات (Rate Limiting) — أمن API وقواعد البيانات بالعربي
تحديد معدل الطلبات (Rate Limiting)أمن API وقواعد البيانات بالعربي · The Code Fix

هل كان هذا الدرس مفيدًا؟