Skip to content

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í