Automatyczne wysyłanie ciasteczek
Przegladarka zawsze wysyła ciasteczka dla domeny, niezależnie od źródła zadania.
This content is not available in your language yet.
Cross-Site Request Forgery (CSRF), czasem nazywany “session riding” lub “one-click attack”, to atak polegajacy na zmuszeniu uwierzytelnionego użytkownika do nieswiadomego wykonania niechcianej akcji w aplikacji webowej. W przeciwienstwie do XSS, gdzie atakujacy wstrzykuje złośliwy kod, CSRF wykorzystuje zaufanie aplikacji do przegladarki użytkownika.
CSRF jest szczegolnie niebezpieczny, ponieważ działa nawet gdy użytkownik jest prawidłowo zalogowany - właściwie to jest wtedy najbardziej skuteczny.
Przegladarka automatycznie dołącza ciasteczka (w tym sesyjne) do każdego zadania wysyłanego do danej domeny. Atakujacy wykorzystuje ten mechanizm:
Automatyczne wysyłanie ciasteczek
Przegladarka zawsze wysyła ciasteczka dla domeny, niezależnie od źródła zadania.
Brak weryfikacji źródła
Serwer nie rozroznia czy zadanie pochodzi z legitymnej strony czy że złośliwej.
Przewidywalnosc żądań
Struktura formularzy i parametrow jest czesto łatwa do odgadniecia.
| Aspekt | CSRF | XSS |
|---|---|---|
| Cel ataku | Wykonanie akcji w imieniu użytkownika | Kradziez danych, wykonanie kodu |
| Wektor | Złośliwa strona zewnętrzna | Kod wstrzykniety w zaufana strone |
| Wymaga logowania | Tak, najbardziej skuteczny | Niekoniecznie |
| Dostep do danych | Nie ma bezposredniego dostepu | Pełny dostep do DOM i ciasteczek |
| Ochrona | Tokeny CSRF, SameSite cookies | Escapowanie, CSP |
Użytkownik loguje się do banku
Użytkownik loguje się na bank.com. Przegladarka otrzymuje ciasteczko sesji.
Użytkownik odwiedza złośliwa strone
W nowej karcie otwiera strone że złośliwym kodem (np. z linku w mailu).
Złośliwa strona wysyła zadanie do banku
<img src="https://bank.com/transfer?to=attacker&amount=10000" />Przegladarka automatycznie dołącza ciasteczka
Zadanie jest wysyłane z ciasteczkiem sesji - dla serwera wyglada jak legitymne.
Bank wykonuje przelew
Serwer widzi prawidłowa sesje i wykonuje operacje.
<!DOCTYPE html><html><head><title>Wygrałeś nagrode!</title></head><body> <h1>Gratulacje! Kliknij aby odebrac nagrode!</h1>
<!-- Ukryty formularz wykonujacy atak --> <form id="csrf-form" action="https://bank.com/transfer" method="POST" style="display:none;"> <input type="hidden" name="recipient" value="attacker-account" /> <input type="hidden" name="amount" value="5000" /> </form>
<script> // Automatyczne wysłanie formularza document.getElementById('csrf-form').submit(); </script></body></html><?php// NIEBEZPIECZNE - brak tokenu CSRFsession_start();
if ($_SERVER['REQUEST_METHOD'] === 'POST') { $newEmail = $_POST['email']; // Zmiana e-maila bez weryfikacji źródła zadania $db->query("UPDATE users SET email = ? WHERE id = ?", [ $newEmail, $_SESSION['user_id'] ]); echo "Email zmieniony!";}?>
<form method="POST"> <input type="email" name="email" placeholder="Nowy email" /> <button type="submit">Zmien email</button></form><?phpsession_start();
// Generowanie tokenu CSRFfunction generateCsrfToken(): string { if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } return $_SESSION['csrf_token'];}
// Weryfikacja tokenu CSRFfunction verifyCsrfToken(string $token): bool { if (empty($_SESSION['csrf_token'])) { return false; } return hash_equals($_SESSION['csrf_token'], $token);}
// Obsługa formularzaif ($_SERVER['REQUEST_METHOD'] === 'POST') { // Sprawdzenie tokenu przed wykonaniem akcji if (!verifyCsrfToken($_POST['csrf_token'] ?? '')) { http_response_code(403); die('Nieprawidłowy token CSRF - możliwy atak!'); }
$newEmail = $_POST['email']; $db->query("UPDATE users SET email = ? WHERE id = ?", [ $newEmail, $_SESSION['user_id'] ]);
// Regeneracja tokenu po uzyciu (opcjonalnie, zwieksza bezpieczeństwo) $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
echo "Email zmieniony!";}
$csrfToken = generateCsrfToken();?>
<form method="POST"> <input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrfToken) ?>" /> <input type="email" name="email" placeholder="Nowy email" /> <button type="submit">Zmien email</button></form>// Pobranie tokenu z meta taguconst csrfToken = document.querySelector('meta[name="csrf-token"]').content;
// Dołączenie tokenu do zadania fetchasync function updateProfile(data) { const response = await fetch('/api/profile', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrfToken }, body: JSON.stringify(data) }); return response.json();}<?phpfunction verifyCsrfFromHeader(): bool { $headerToken = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? ''; return verifyCsrfToken($headerToken);}Najpopularniejsza metoda - każdy formularz zawiera unikalny, nieprzewidywalny token.
<?phpsetcookie('session_id', $sessionId, [ 'samesite' => 'Strict', // lub 'Lax' 'httponly' => true, 'secure' => true]);| Wartość | Zachowanie |
|---|---|
| Strict | Ciasteczko tylko dla żądań z tej samej strony |
| Lax | Pozwala na top-level navigation (linki), blokuje POST |
| None | Brak ochrony (wymaga Secure) |
<?phpfunction verifyOrigin(): bool { $origin = $_SERVER['HTTP_ORIGIN'] ?? ''; $referer = $_SERVER['HTTP_REFERER'] ?? ''; $allowedOrigin = 'https://myapp.com';
return str_starts_with($origin, $allowedOrigin) || str_starts_with($referer, $allowedOrigin);}Token jest wysyłany zarowno w ciasteczku jak i w formularzu - atakujacy nie może ustawic ciasteczka dla innej domeny.
CSRF to podstepny atak wykorzystujacy mechanizm automatycznego wysyłania ciasteczek przez przegladarke. Skuteczna ochrona wymaga:
Pamiętaj: Samo logowanie nie chroni przed CSRF - właśnie zalogowani użytkownicy sa celem tego ataku.