وقتی یک سیستم ورود، ارسال کد OTP، بازیابی رمز عبور یا API طراحی میکنیم، فقط بررسی صحیح بودن اطلاعات ورودی کافی نیست. اگر کاربر یا مهاجم بتواند بدون محدودیت هزاران درخواست در مدت کوتاه ارسال کند، سیستم میتواند در معرض حملاتی مانند Brute Force، سوءاستفاده از OTP، Password Spraying و درخواستهای بیش از حد API قرار بگیرد.
یکی از مهمترین راهکارها برای کنترل این وضعیت، Rate Limiting است.
Rate Limiting به زبان ساده یعنی:
> برای یک کاربر، IP، API Key یا Endpoint مشخص، در یک بازه زمانی فقط تعداد مشخصی درخواست اجازه دهیم.
برای مثال میتوانیم تعیین کنیم:
- هر IP حداکثر ۵ تلاش ورود در ۱۵ دقیقه داشته باشد.
- ارسال OTP حداکثر ۳ بار در ۱۰ دقیقه انجام شود.
- بررسی کد OTP حداکثر ۵ بار در ۵ دقیقه انجام شود.
- یک API Key در هر دقیقه حداکثر ۱۰۰ درخواست ارسال کند.
در این مقاله ابتدا مفهوم Rate Limiting را بررسی میکنیم و سپس چند روش عملی برای پیادهسازی آن در PHP، MySQL و Redis میسازیم.
Rate Limiting چیست؟
Rate Limiting مکانیزمی برای محدود کردن تعداد درخواستهایی است که یک Client میتواند در یک بازه زمانی مشخص به یک سیستم ارسال کند.
فرض کنید Endpoint زیر را داریم:
POST /login
اگر هیچ محدودیتی نداشته باشیم، مهاجم میتواند تعداد بسیار زیادی درخواست ارسال کند:
Request 1
Request 2
Request 3
...
Request 10000
اما با Rate Limiting میتوانیم مثلاً قانون زیر را تعریف کنیم:
حداکثر 5 درخواست در 15 دقیقه
بعد از پنجمین درخواست، درخواستهای بعدی موقتاً مسدود میشوند.
Rate Limiting چه تفاوتی با Throttling دارد؟
این دو اصطلاح گاهی به جای یکدیگر استفاده میشوند، اما میتوان بین آنها تفاوت مفهومی قائل شد.
Rate Limiting
مشخص میکند در یک بازه زمانی چه تعداد درخواست مجاز است.
مثلاً:
100 requests / minute
Throttling
بیشتر روی کنترل یا کاهش سرعت پردازش درخواستها تمرکز دارد.
برای مثال ممکن است سیستم به جای رد کردن کامل درخواستها، سرعت پاسخگویی یا پردازش را کاهش دهد.
در بسیاری از فریمورکها و سیستمهای وب، این دو مفهوم در کنار یکدیگر استفاده میشوند.
# چرا Rate Limiting در PHP مهم است؟
Rate Limiting فقط برای Login نیست.
در یک سیستم واقعی میتوان از آن برای Endpointهای مختلف استفاده کرد.
۱. Login
برای کاهش ریسک:
- Brute Force
- Password Spraying
- Credential Stuffing
۲. OTP
برای محدود کردن:
- درخواست ارسال OTP
- تلاش برای حدس زدن OTP
- ارسال مجدد کد
۳. Password Reset
برای جلوگیری از:
- ارسال تعداد زیادی ایمیل
- سوءاستفاده از فرم Forgot Password
- Email Enumeration
۴. Email Verification
برای جلوگیری از درخواستهای مکرر ارسال ایمیل تأیید.
۵. API
برای کنترل تعداد درخواستهای Clientها.
۶. فرمهای حساس
مانند:
Login
Register
Contact
Password Reset
Payment
OTP
Search
API
# Rate Limiting چگونه کار میکند؟
برای پیادهسازی Rate Limiting معمولاً باید چند اطلاعات را ذخیره کنیم.
برای مثال:
Key
Count
Window Start
Blocked Until
فرض کنید:
key = login:192.168.1.10
count = 5
window_start = 10:00
blocked_until = 10:15
سیستم میداند این IP در پنجره فعلی ۵ درخواست داشته است.
# Rate Limit را بر اساس چه چیزی اعمال کنیم؟
یکی از مهمترین تصمیمها این است که محدودیت را بر چه مبنایی اعمال کنیم.
محدودیت بر اساس IP
مثلاً:
login:ip:192.168.1.10
مزیت:
ساده و سریع است.
مشکل:
چندین کاربر ممکن است از یک IP استفاده کنند.
مثلاً:
- شرکت
- دانشگاه
- اینترنت موبایل
- NAT
بنابراین نباید همیشه فقط IP را معیار قرار دهیم.
محدودیت بر اساس User
مثلاً:
login:user:125
این روش برای کاربران احراز هویتشده بسیار مناسب است.
محدودیت بر اساس Email
مثلاً:
login:email:user@example.com
اما در Login باید مراقب User Enumeration باشیم.
نباید پاسخ سیستم به شکلی باشد که مهاجم بتواند تشخیص دهد یک Email در سیستم وجود دارد یا خیر.
محدودیت ترکیبی
برای Login معمولاً میتوان چند محدودیت را همزمان در نظر گرفت:
IP + Email
مثلاً:
login:ip:192.168.1.10
login:email:user@example.com
این روش از اتکا به یک معیار واحد بهتر است.
# الگوریتمهای Rate Limiting
روشهای مختلفی برای Rate Limiting وجود دارد.
مهمترین آنها:
- Fixed Window
- Sliding Window
- Token Bucket
- Leaky Bucket
# Fixed Window
در این روش یک بازه زمانی ثابت تعریف میکنیم.
مثلاً:
5 requests / 15 minutes
فرض کنیم پنجره از ساعت 10:00 شروع شود:
10:00 → 10:15
کاربر در این بازه حداکثر ۵ درخواست دارد.
مزیت
پیادهسازی سادهای دارد.
مشکل
در مرز پنجره ممکن است تعداد زیادی درخواست در مدت کوتاه وارد شود.
# Sliding Window
در Sliding Window به جای یک پنجره ثابت، درخواستهای یک بازه متحرک بررسی میشوند.
مثلاً:
5 requests during the last 15 minutes
اگر کاربر در ساعت 10:14 پنج درخواست ارسال کرده باشد، هنوز در ساعت 10:15 لزوماً همه محدودیتها به صورت ناگهانی Reset نمیشوند.
این روش کنترل دقیقتری ایجاد میکند اما پیادهسازی آن پیچیدهتر است.
# Token Bucket
در Token Bucket تعدادی Token در یک Bucket قرار میگیرد.
هر درخواست یک Token مصرف میکند.
اگر Token باقی نمانده باشد:
Request → Reject
Tokenها با گذشت زمان دوباره ایجاد میشوند.
این روش برای APIها و سیستمهایی که به Burst کنترلشده نیاز دارند بسیار کاربردی است.
# Leaky Bucket
در این روش درخواستها وارد یک صف میشوند و با نرخ مشخصی پردازش میشوند.
در نتیجه سیستم تلاش میکند جریان درخواستها را با سرعت کنترلشده پردازش کند.
# سادهترین Rate Limiting در PHP
برای یادگیری میتوانیم ابتدا یک نمونه ساده با Session بسازیم.
<?php
session_start();
$limit = 5;
$window = 60;
$now = time();
if (!isset($_SESSION['rate_limit'])) {
$_SESSION['rate_limit'] = [
'count' => 0,
'start' => $now,
];
}
$data = &$_SESSION['rate_limit'];
if (($now - $data['start']) >= $window) {
$data['count'] = 0;
$data['start'] = $now;
}
$data['count']++;
if ($data['count'] > $limit) {
http_response_code(429);
exit('Too Many Requests');
}
echo 'Request allowed';
در این مثال:
$limit = 5;
یعنی حداکثر ۵ درخواست.
و:
$window = 60;
یعنی پنجره زمانی ۶۰ ثانیه.
# HTTP Status Code 429 چیست؟
وقتی Client از محدودیت درخواست عبور کند، Status Code استاندارد زیر کاربرد دارد:
429 Too Many Requests
مثلاً:
http_response_code(429);
echo 'Too Many Requests';
میتوانیم اطلاعات بیشتری هم ارسال کنیم.
header('Retry-After: 60');
http_response_code(429);
echo 'Please try again later.';
Retry-After به Client اعلام میکند که چه مدت بهتر است قبل از تلاش مجدد صبر کند.
# چرا Session برای Rate Limiting واقعی کافی نیست؟
مثال قبلی برای یادگیری خوب است اما برای سیستم Production محدودیتهایی دارد.
فرض کنید چند سرور داریم:
Server 1
Server 2
Server 3
اگر Rate Limit داخل Session محلی ذخیره شود، ممکن است هر سرور وضعیت متفاوتی داشته باشد.
همچنین Session بیشتر برای همان Client مناسب است و برای Rate Limiting سراسری انتخاب ایدهآلی نیست.
برای سیستمهای واقعی معمولاً از:
- Database
- Redis
استفاده میشود.
# ساخت Rate Limiting با MySQL
یک جدول ساده ایجاد میکنیم:
CREATE TABLE rate_limits (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
rate_key VARCHAR(255) NOT NULL UNIQUE,
attempts INT UNSIGNED NOT NULL DEFAULT 0,
window_start DATETIME NOT NULL,
blocked_until DATETIME NULL,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL
);
ستون rate_key مشخص میکند محدودیت مربوط به چه چیزی است.
مثلاً:
login:ip:192.168.1.10
یا:
otp:email:user@example.com
# اتصال PHP به MySQL با PDO
<?php
$pdo = new PDO(
'mysql:host=127.0.0.1;dbname=myapp;charset=utf8mb4',
'root',
'password'
);
$pdo->setAttribute(
PDO::ATTR_ERRMODE,
PDO::ERRMODE_EXCEPTION
);
# ساخت Rate Limiter ساده با Database
<?php
function checkRateLimit(
PDO $pdo,
string $key,
int $maxAttempts,
int $windowSeconds
): bool {
$now = new DateTimeImmutable();
$stmt = $pdo->prepare(
'SELECT *
FROM rate_limits
WHERE rate_key = ?
LIMIT 1'
);
$stmt->execute([$key]);
$record = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$record) {
$stmt = $pdo->prepare(
'INSERT INTO rate_limits
(
rate_key,
attempts,
window_start,
created_at,
updated_at
)
VALUES (?, 1, ?, ?, ?)'
);
$date = $now->format('Y-m-d H:i:s');
$stmt->execute([
$key,
$date,
$date,
$date,
]);
return true;
}
$windowStart = new DateTimeImmutable(
$record['window_start']
);
$elapsed = $now->getTimestamp()
- $windowStart->getTimestamp();
if ($elapsed >= $windowSeconds) {
$stmt = $pdo->prepare(
'UPDATE rate_limits
SET attempts = 1,
window_start = ?,
blocked_until = NULL,
updated_at = ?
WHERE rate_key = ?'
);
$date = $now->format('Y-m-d H:i:s');
$stmt->execute([
$date,
$date,
$key,
]);
return true;
}
if ($record['attempts'] >= $maxAttempts) {
return false;
}
$stmt = $pdo->prepare(
'UPDATE rate_limits
SET attempts = attempts + 1,
updated_at = ?
WHERE rate_key = ?'
);
$stmt->execute([
$now->format('Y-m-d H:i:s'),
$key,
]);
return true;
}
استفاده:
$ip = $_SERVER['REMOTE_ADDR'];
$key = 'login:ip:' . $ip;
if (!checkRateLimit($pdo, $key, 5, 900)) {
http_response_code(429);
exit('Too many login attempts.');
}
در این مثال:
5 attempts
900 seconds
یعنی:
5 تلاش در 15 دقیقه
اعداد بالا صرفاً نمونه هستند و باید بر اساس رفتار واقعی سیستم تنظیم شوند.
# یک نکته مهم درباره Race Condition
در سیستمهای همزمان، این روش ساده میتواند در شرایط خاص دچار Race Condition شود.
مثلاً دو Request تقریباً همزمان وارد شوند:
Request A → SELECT
Request B → SELECT
هر دو ممکن است مقدار قبلی را ببینند.
سپس هر دو مقدار را افزایش دهند.
برای سیستمهای پرترافیک بهتر است عملیات افزایش Counter تا حد امکان به صورت Atomic انجام شود.
برای مثال در MySQL میتوان از الگوهایی مانند:
INSERT ... ON DUPLICATE KEY UPDATE
استفاده کرد یا عملیات حساس را با Transaction و قفل مناسب طراحی کرد.
# Rate Limiting برای Login
حالا یک سناریوی واقعیتر را بررسی کنیم.
فرض کنید کاربر به این Endpoint درخواست میدهد:
POST /login
اطلاعات:
email
password
داریم.
میتوانیم برای Login چند محدودیت مختلف داشته باشیم.
مثلاً:
IP → 20 requests / 15 minutes
Email + IP → 5 attempts / 15 minutes
این ساختار بهتر از محدود کردن صرفاً بر اساس IP است.
# چرا فقط IP کافی نیست؟
فرض کنید یک شرکت ۵۰ کارمند دارد.
همه از یک IP عمومی استفاده میکنند:
203.0.113.10
اگر بگوییم:
5 login attempts / 15 minutes / IP
ممکن است بعد از چند تلاش اشتباه، کاربران واقعی آن شرکت نیز محدود شوند.
به همین دلیل Rate Limiting باید با توجه به نوع Endpoint طراحی شود.
# Rate Limiting برای OTP
OTP یکی از حساسترین قسمتهای سیستم احراز هویت است.
دو محدودیت متفاوت میتوانیم داشته باشیم:
محدودیت ارسال OTP
مثلاً:
3 SMS / 10 minutes
محدودیت بررسی OTP
مثلاً:
5 attempts / 5 minutes
این دو مورد را نباید با یک Counter ساده اشتباه گرفت.
# نمونه Rate Limit برای OTP
فرض کنیم:
$email = strtolower(trim($_POST['email']));
$key = 'otp:verify:' . hash(
'sha256',
$email
);
if (!checkRateLimit($pdo, $key, 5, 300)) {
http_response_code(429);
exit('Too many OTP attempts.');
}
در اینجا:
5 attempts
300 seconds
یعنی حداکثر ۵ تلاش در ۵ دقیقه.
اما Rate Limiting به تنهایی کافی نیست.
OTP باید:
- Expire شود.
- پس از استفاده باطل شود.
- به صورت امن ذخیره شود.
- تعداد تلاشهای ناموفق داشته باشد.
- در برابر Brute Force محافظت شود.
# Rate Limiting برای Password Reset
فرم:
Forgot Password
نیز باید محدود شود.
برای مثال:
3 درخواست / 15 دقیقه / IP
و همچنین:
3 درخواست / 15 دقیقه / Email
اما پاسخ سیستم نباید مشخص کند که Email وجود دارد یا خیر.
پاسخ عمومی بهتر است:
اگر حسابی با این اطلاعات وجود داشته باشد،
دستورالعمل بازیابی رمز عبور ارسال خواهد شد.
این موضوع از User Enumeration جلوگیری میکند.
# Rate Limiting برای API
در API معمولاً Rate Limit بر اساس یکی از موارد زیر تعیین میشود:
API Key
User ID
IP
Client ID
مثلاً:
100 requests / minute
اگر محدودیت رد شود:
http_response_code(429);
header('Retry-After: 30');
echo json_encode([
'message' => 'Too Many Requests'
]);
# Headerهای Rate Limit
در APIها میتوان اطلاعات محدودیت را نیز به Client اعلام کرد.
برای مثال:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 12
X-RateLimit-Reset: 1750000000
البته نام و نحوه استفاده از Headerها باید با قرارداد API شما هماهنگ باشد.
# استفاده از Redis برای Rate Limiting
برای سیستمهای Production و مخصوصاً زمانی که چند سرور دارید، Redis گزینه بسیار مناسبی است.
مثلاً:
Server 1
Server 2
Server 3
↓
Redis
تمام سرورها میتوانند وضعیت Rate Limit را از یک منبع مشترک بخوانند.
# مثال ساده با Redis
فرض کنیم Redis در PHP در دسترس است:
<?php
$key = 'rate:login:ip:' . $ip;
$count = $redis->incr($key);
if ($count === 1) {
$redis->expire($key, 60);
}
if ($count > 5) {
http_response_code(429);
exit('Too Many Requests');
}
در این مثال:
5 requests
60 seconds
داریم.
# یک نکته مهم درباره INCR و EXPIRE
کد:
$count = $redis->incr($key);
if ($count === 1) {
$redis->expire($key, 60);
}
برای آموزش مناسب است، اما در سیستمهای بسیار حساس باید Atomic بودن کل منطق را جدی بگیریم.
اگر برنامه دقیقاً بین:
INCR
و:
EXPIRE
متوقف شود، ممکن است Key بدون TTL باقی بماند.
برای Production میتوان از:
- Redis Lua Script
- Transaction
- Pipeline مناسب
- الگوریتمهای Atomic
استفاده کرد.
# ساخت Key مناسب در Redis
بهتر است Keyها Namespace داشته باشند.
مثلاً:
rate:login:ip:203.0.113.10
یا:
rate:otp:email:HASH
یا:
rate:api:user:125
استفاده از Namespace باعث میشود مدیریت Keyها سادهتر شود.
# آیا IP را مستقیماً در Redis ذخیره کنیم؟
در برخی سیستمها میتوان IP را در Key استفاده کرد، اما برای بعضی کاربردها بهتر است شناسه را Hash کنیم.
مثلاً:
$identifier = hash(
'sha256',
$ip
);
$key = 'rate:login:ip:' . $identifier;
در هر صورت باید سیاست نگهداری داده، Logging و حریم خصوصی پروژه را نیز در نظر بگیرید.
# مشکل X-Forwarded-For
یکی از اشتباهات رایج این است که IP را بدون بررسی از Headerهایی مانند:
X-Forwarded-For
بگیریم.
این Header میتواند توسط Client جعل شود.
اگر برنامه پشت:
- Nginx
- Load Balancer
- Cloud Proxy
- Reverse Proxy
قرار دارد، باید فقط Proxyهای مورد اعتماد خود را در نظر بگیریم و IP واقعی را طبق تنظیمات همان زیرساخت استخراج کنیم.
نباید به شکل کورکورانه بنویسیم:
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
# Rate Limiting و Brute Force
Brute Force یعنی مهاجم تلاش میکند با امتحان کردن تعداد زیادی ترکیب، اطلاعات احراز هویت را پیدا کند.
مثلاً:
password1
password2
password3
...
Rate Limiting سرعت این حملات را کاهش میدهد.
اما Rate Limiting به تنهایی یک راهکار کامل نیست.
برای Login بهتر است در کنار آن از مواردی مانند:
- Password Hashing
- Password Policy
- MFA / 2FA
- Session Security
- Logging
- Monitoring
- CAPTCHA در شرایط خاص
- Detection رفتار غیرعادی
استفاده شود.
# Rate Limiting و Account Lockout
ممکن است تصور کنیم بهترین راه این است:
5 failed login
↓
Lock account
اما این روش میتواند مشکل دیگری ایجاد کند.
مهاجم میتواند عمداً برای حساب قربانی چند Login اشتباه ارسال کند تا حساب او قفل شود.
این نوع سوءاستفاده میتواند به یک حمله DoS روی حساب تبدیل شود.
به همین دلیل در بسیاری از سیستمها به جای Lockout دائمی، از ترکیبی مانند:
Rate Limit
+
Progressive Delay
+
Temporary Cooldown
+
Monitoring
استفاده میشود.
# Progressive Delay چیست؟
به جای مسدود کردن ناگهانی، میتوان زمان انتظار را افزایش داد.
مثلاً:
Attempt 1 → بدون تأخیر
Attempt 2 → بدون تأخیر
Attempt 3 → 2 ثانیه
Attempt 4 → 5 ثانیه
Attempt 5 → 10 ثانیه
این روش میتواند سرعت حملات خودکار را کاهش دهد بدون اینکه یک حساب برای مدت طولانی کاملاً قفل شود.
# CAPTCHA جایگزین Rate Limiting نیست
گاهی تصور میشود اگر CAPTCHA داشته باشیم دیگر نیازی به Rate Limiting نداریم.
این تصور درست نیست.
CAPTCHA میتواند یک لایه دفاعی اضافی باشد، اما Rate Limiting همچنان برای کنترل حجم درخواستها مفید است.
ساختار مناسب میتواند چیزی شبیه این باشد:
Request
↓
Rate Limit
↓
Risk Detection
↓
CAPTCHA در صورت نیاز
↓
Authentication
# Rate Limiting در Laravel
اگر بعداً همین مفهوم را در Laravel پیادهسازی کنید، Laravel امکانات داخلی مناسبی برای Rate Limiting دارد.
برای مثال میتوان Limitهایی مانند:
Limit::perMinute(5)
تعریف کرد و آنها را به Route یا Middleware متصل کرد.
اما در PHP خام، باید خودمان State، Storage، Window و رفتار هنگام عبور از Limit را طراحی کنیم.
بنابراین یادگیری مفهوم Rate Limiting در PHP خام باعث میشود هنگام استفاده از ابزارهای Laravel نیز درک بهتری از پشت صحنه داشته باشید.
# اشتباهات رایج در Rate Limiting
۱. محدود کردن همه چیز با IP
این کار میتواند کاربران واقعی پشت NAT را نیز محدود کند.
۲. نداشتن Rate Limit برای OTP
OTP یکی از مهمترین Endpointهای حساس است.
۳. استفاده از Session برای سیستم چندسروره
در معماری چندسروره بهتر است State مشترک داشته باشیم.
Redis یکی از گزینههای مناسب است.
۴. استفاده نادرست از X-Forwarded-For
نباید Headerهای Proxy را بدون تنظیمات Trusted Proxy معتبر فرض کنیم.
۵. قفل دائمی حساب
Lockout شدید میتواند خودش تبدیل به ابزار DoS شود.
۶. Rate Limiting فقط روی Login
Endpointهای زیر نیز ممکن است نیازمند محدودیت باشند:
OTP
Password Reset
Email Verification
Register
API
Contact
Search
Payment
۷. ذخیره اطلاعات حساس در Log
هرگز مواردی مانند:
Password
OTP
Reset Token
Access Token
را بدون دلیل و به شکل خام Log نکنید.
# Rate Limiting در معماری چندسروره
فرض کنیم برنامه شما این معماری را دارد:
Load Balancer
|
+---------+---------+
| | |
Server 1 Server 2 Server 3
| | |
+---------+---------+
|
Redis
اگر Rate Limit داخل حافظه Server 1 ذخیره شود، Server 2 از آن اطلاعی ندارد.
بنابراین مهاجم میتواند درخواستها را بین سرورها پخش کند.
اما اگر State مشترک در Redis باشد:
Server 1 ─┐
Server 2 ─┼── Redis
Server 3 ─┘
تمام سرورها Counter مشترکی خواهند داشت.
# طراحی پیشنهادی برای یک سیستم واقعی
برای یک سیستم PHP واقعی میتوان معماری زیر را در نظر گرفت:
Request
↓
Authentication
↓
Rate Limiter
↓
┌───────────┴───────────┐
↓ ↓
Redis Database
↓
Counter / TTL
↓
Allowed / Blocked
برای Rate Limitهای پرتعداد و سریع:
Redis
معمولاً گزینه مناسبی است.
برای اطلاعات دائمی و Audit:
Database
مناسبتر است.
# Rate Limit مناسب را چگونه انتخاب کنیم؟
یک عدد ثابت برای همه سیستمها وجود ندارد.
مثلاً:
Login
OTP
API
Search
Upload
همگی رفتار متفاوتی دارند.
بنابراین باید بر اساس مواردی مانند:
- تعداد کاربران
- نوع Endpoint
- حساسیت عملیات
- الگوی مصرف
- تعداد سرورها
- ظرفیت زیرساخت
- False Positive
- تجربه کاربری
مقدار مناسب را تعیین کرد.
مثلاً این اعداد صرفاً نمونه آموزشی هستند:
| عملیات | نمونه محدودیت |
|---|---|
| Login | 5 تلاش / 15 دقیقه |
| OTP Verify | 5 تلاش / 5 دقیقه |
| OTP Resend | 3 درخواست / 10 دقیقه |
| Password Reset | 3 درخواست / 15 دقیقه |
| API | 100 درخواست / دقیقه |
این مقادیر قانون عمومی امنیتی نیستند و باید برای هر سیستم جداگانه تنظیم شوند.
# چکلیست پیادهسازی Rate Limiting در PHP
قبل از انتشار سیستم، این موارد را بررسی کنید:
- [ ] برای Login محدودیت تعریف شده است.
- [ ] برای OTP محدودیت وجود دارد.
- [ ] OTP Verify محدودیت جداگانه دارد.
- [ ] Password Reset محدود شده است.
- [ ] Email Verification محدود شده است.
- [ ] APIها Rate Limit دارند.
- [ ] IP تنها معیار Rate Limit نیست.
- [ ] User Enumeration کنترل شده است.
- [ ] پاسخ 429 برای عبور از Limit استفاده میشود.
- [ ] در صورت نیاز
Retry-Afterارسال میشود. - [ ] در معماری چندسروره State مشترک وجود دارد.
- [ ] Redis یا Storage مناسب انتخاب شده است.
- [ ] Race Condition در عملیات Counter بررسی شده است.
- [ ] Proxyهای مورد اعتماد برای تشخیص IP تنظیم شدهاند.
- [ ] Password، OTP و Token در Log ذخیره نمیشوند.
- [ ] Lockout باعث DoS روی حسابها نمیشود.
- [ ] Rate Limitها Monitoring میشوند.
# پرسشهای متداول
Rate Limiting در PHP چیست؟
Rate Limiting مکانیزمی است که تعداد درخواستهای مجاز یک Client، IP، User یا Endpoint را در یک بازه زمانی محدود میکند.
آیا Rate Limiting جلوی Brute Force را به طور کامل میگیرد؟
خیر.
Rate Limiting سرعت و حجم تلاشهای مهاجم را محدود میکند، اما باید در کنار Password Hashing، 2FA، Session Security و سایر لایههای امنیتی استفاده شود.
آیا میتوان Rate Limiting را فقط با IP انجام داد؟
بله، اما برای بسیاری از سیستمها IP به تنهایی معیار مناسبی نیست. بهتر است بسته به Endpoint از ترکیب IP، User، Email، API Key یا سایر شناسهها استفاده شود.
Redis بهتر است یا MySQL؟
هر دو میتوانند استفاده شوند.
برای Counterهای سریع و سیستمهای چندسروره، Redis معمولاً گزینه مناسبی است.
MySQL نیز برای سیستمهایی که به Persistence و گزارشگیری نیاز دارند کاربرد دارد.
HTTP 429 چیست؟
کد:
429 Too Many Requests
برای زمانی استفاده میشود که Client در مدت مشخصی بیش از تعداد مجاز درخواست ارسال کرده باشد.
آیا CAPTCHA جای Rate Limiting را میگیرد؟
خیر.
CAPTCHA و Rate Limiting دو لایه متفاوت هستند و میتوانند در کنار یکدیگر استفاده شوند.
# جمعبندی
Rate Limiting یکی از اجزای مهم امنیت یک برنامه PHP است و باید برای Endpointهای حساس از همان مرحله طراحی سیستم در نظر گرفته شود.
مواردی مانند:
Login
OTP
Password Reset
Email Verification
API
نباید بدون محدودیت درخواست در اختیار Client قرار بگیرند.
برای پروژههای ساده میتوان از Session یا Database استفاده کرد، اما در معماریهای چندسروره و پرترافیک بهتر است State مربوط به Rate Limit در یک Storage مشترک مانند Redis نگهداری شود.
همچنین نباید Rate Limiting را فقط به یک IP محدود کنیم. طراحی مناسب معمولاً ترکیبی از چند معیار مانند IP، User، Email یا API Key است.
در نهایت Rate Limiting یک لایه دفاعی است، نه یک راهکار امنیتی مستقل. برای ساخت سیستم احراز هویت امن باید آن را در کنار Password Hashing، Session Security، CSRF Protection، 2FA، مدیریت صحیح Tokenها و Logging/Monitoring استفاده کرد.
مقاله پیشنهادی بعدی
بعد از یادگیری Rate Limiting، موضوع منطقی بعدی میتواند Brute Force در PHP باشد؛ در آن مقاله میتوانیم حملات Brute Force، Password Spraying، Credential Stuffing، Account Lockout، Progressive Delay و طراحی سیستم دفاعی Login را عمیقتر بررسی کنیم.





