RateLimit – Příklady Použití
Instalace & Setup
RateLimiter je součástí core. Nenabízí se žádný setup – jen používej:
use Petrovo\Security\RateLimiter;
// Výchozí storage: var/rate-limits/
RateLimiter::check($ip, 20, 60);
Pokud chceš vlastní cestu:
// Bootstrap aplikace
RateLimiter::setPath('/var/cache/my-rate-limits');
Případ 1: Ochrana Login Formuláře
<?php
// app/routes/login.php (POST /login)
use Petrovo\Security\RateLimiter;
$ip = $_SERVER['REMOTE_ADDR'];
// Ověř limit: 10 pokusů za 5 minut
if (!RateLimiter::check($ip, 10, 300)) {
http_response_code(429);
exit('Příliš mnoho pokusů. Zkus za 5 minut.');
}
// OK – zpracuj login
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
if (authenticate($username, $password)) {
// Úspěšný login – smaž limit (dej mu čistý štít)
RateLimiter::reset($ip);
// Set session & redirect
$_SESSION['user_id'] = $user->id;
header('Location: /dashboard');
} else {
// Neúspěšný pokus – limit zůstává
echo 'Špatné heslo';
}
Výhody:
- Útočník může zkoušet jen 10x za 5 minut
- Legitimní uživatel (s jedním špatným heslem) se nedostane na 10 pokusů
- Po úspěchu se limit resetuje → nebude mu blokován příští login
Případ 2: API Endpoint Limitování
<?php
// app/routes/api/users.php (GET /api/users)
use Petrovo\Data;
use Petrovo\Security\RateLimiter;
$ip = $_SERVER['REMOTE_ADDR'];
// API je veřejný – přísný limit
if (!RateLimiter::check($ip, 50, 60)) {
http_response_code(429);
return Data\outputJson([
'error' => 'Rate limit exceeded',
'message' => 'Max 50 requests per minute'
], 429);
}
// OK – vrátit data
$users = DB::results("SELECT id, name FROM users LIMIT 100");
return Data\outputJson(['users' => $users]);
Případ 3: Veřejný Kontakt Formulář
<?php
// app/routes/contact.php (POST /contact)
use Petrovo\Security\RateLimiter;
$ip = $_SERVER['REMOTE_ADDR'];
// Veřejný formulář – velmi přísně (spam prevention)
if (!RateLimiter::check($ip, 3, 3600)) { // 3x za hodinu
http_response_code(429);
exit('Příliš mnoho zpráv. Zkus později.');
}
// OK – uložit formulář
$name = $_POST['name'] ?? '';
$email = $_POST['email'] ?? '';
$message = $_POST['message'] ?? '';
// ... validation & save ...
echo 'Děkuji za zprávu!';
Případ 4: Password Reset
<?php
// app/routes/password-reset.php (POST /password-reset/request)
use Petrovo\Security\RateLimiter;
$ip = $_SERVER['REMOTE_ADDR'];
// Password reset je cílená akce – přísně
if (!RateLimiter::check($ip, 5, 3600)) { // 5x za hodinu
http_response_code(429);
exit('Příliš mnoho pokusů o reset. Zkus za hodinu.');
}
// OK – odešli email s reset linkem
$email = $_POST['email'] ?? '';
send_password_reset_email($email);
echo 'Kontroluj svůj email.';
Případ 5: Monitorování & Admin Reporting
<?php
// app/Controllers/AdminController.php
public function suspiciousActivity()
{
// Zjisti které IP mají vysokou aktivitu
$rateLimitDir = DIR . '/var/rate-limits';
$suspicious = [];
foreach (glob($rateLimitDir . '/*.json') as $file) {
$data = json_decode(file_get_contents($file), true);
// IP která má víc než 50 requestů za minutu
if (count($data['requests'] ?? []) > 50) {
$suspicious[] = [
'hash' => basename($file, '.json'),
'count' => count($data['requests']),
'last_request' => max($data['requests']) ?? 0
];
}
}
return $suspicious;
}
Případ 6: Middleware Integrace (Selektivní)
<?php
// app/Middlewares/rateLimitLoginMiddleware.php
use Petrovo\Security\RateLimiter;
return static function ($request, $next) {
$path = $request->getUri()->getPath();
$method = $request->getMethod();
$ip = $_SERVER['REMOTE_ADDR'];
// Aplikuj limit POUZE na login endpoint
if ($method === 'POST' && $path === '/login') {
if (!RateLimiter::check($ip, 10, 300)) {
http_response_code(429);
exit('Příliš mnoho pokusů. Zkus za 5 minut.');
}
}
return $next($request);
};
Nebo pro API endpointy:
<?php
// app/Middlewares/rateLimitApiMiddleware.php
return static function ($request, $next) {
$path = $request->getUri()->getPath();
$ip = $_SERVER['REMOTE_ADDR'];
// API path
if (str_starts_with($path, '/api/')) {
if (!RateLimiter::check($ip, 100, 60)) {
http_response_code(429);
exit('Rate limit exceeded');
}
}
return $next($request);
};
Případ 7: Admin Reset Limitu
<?php
// User funkce – resetuj limit pro uživatele
if (!is_admin()) exit('Forbidden');
$ip = $_GET['ip'] ?? '';
if (!$ip) exit('Missing IP parameter');
// Validate IP format (basic)
if (!filter_var($ip, FILTER_VALIDATE_IP)) {
exit('Invalid IP');
}
if (RateLimiter::reset($ip)) {
echo "Limit resetován pro $ip";
// Log to audit
error_log("User reset rate limit: $ip");
} else {
echo "Chyba při resetu";
}
Případ 8: Debugging – Zjisti Aktuální Stav
<?php
// Debug script
use Petrovo\Security\RateLimiter;
$ip = '192.168.1.100';
// Kolik requestů aktuálně?
$count = RateLimiter::getCount($ip, 60);
echo "Requests v poslední minutě: $count\n";
// Zkus check (může být true či false)
$allowed = RateLimiter::check($ip, 10, 60);
echo "Povoleno? " . ($allowed ? 'ANO' : 'NE') . "\n";
// Smaž limit
RateLimiter::reset($ip);
echo "Limit smazán\n";
Best Practices
✅ DO
- Volej
check()jen tam, kde to má smysl (login, API, forms) - Resetuj limit po úspěšné akci (login, submit formuláře)
- Logguj podezřelou aktivitu (vysoký počet requestů)
- Nastav realistické limity (podívej se na reální traffic)
❌ DON'T
- ❌ Nevolej
check()globálně na každý request (kvůli výkonu) - ❌ Nevolej
check()na statické soubory (.js, .css, .png) - ❌ Nevolej
check()na favicon, robots.txt, sitemap - ❌ Neměň limity při každém requestu (udržuj konzistenci)
🎯 Doporučené Limity
// Login form – dost přísný
RateLimiter::check($ip, 10, 300); // 10 za 5 minut
// Password reset – velmi přísný
RateLimiter::check($ip, 3, 3600); // 3 za hodinu
// API – volné
RateLimiter::check($ip, 100, 60); // 100 za minutu
// Veřejný formulář – přísný
RateLimiter::check($ip, 5, 3600); // 5 za hodinu
Troubleshooting
"var/rate-limits není zapisovatelný"
Řešení: chmod 0755 var/rate-limits
Nebo: Nastav vlastní path: RateLimiter::setPath('/tmp/rate-limits')
"RateLimiter zpomaluje login"
❌ Voláš na všechny requesty (middleware globálně)
✅ Volej jen na POST /login endpoint
"Limit se neresetuje po úspěchu"
Ujisti se, že voláš reset() po auth:
if ($login_success) {
RateLimiter::reset($ip); // ← NUTNÉ
}
"Soubory se neroztuhují"
Cleanup běží ~1% requestů (lazy cleanup)
Pokud máš málo requestů, cleanup se spouští vzácně
Případně ručně smažte: rm -f var/rate-limits/*.json
Pokročilé: Vlastní Cleanup
<?php
// Manuální cleanup (např. v cronjob)
$dir = DIR . '/var/rate-limits';
$threshold = time() - 3600; // Smaž starší 1h
foreach (glob($dir . '/*.json') as $file) {
if (filemtime($file) < $threshold) {
unlink($file);
}
}
echo "Cleanup complete";
Další Čtení
- Přehled – Jak to funguje
- API Reference – Všechny funkce