Skip to content

CSRF - ataki i zabezpieczenia

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.

AspektCSRFXSS
Cel atakuWykonanie akcji w imieniu użytkownikaKradziez danych, wykonanie kodu
WektorZłośliwa strona zewnętrznaKod wstrzykniety w zaufana strone
Wymaga logowaniaTak, najbardziej skutecznyNiekoniecznie
Dostep do danychNie ma bezposredniego dostepuPełny dostep do DOM i ciasteczek
OchronaTokeny CSRF, SameSite cookiesEscapowanie, CSP
  1. Użytkownik loguje się do banku

    Użytkownik loguje się na bank.com. Przegladarka otrzymuje ciasteczko sesji.

  2. Użytkownik odwiedza złośliwa strone

    W nowej karcie otwiera strone że złośliwym kodem (np. z linku w mailu).

  3. Złośliwa strona wysyła zadanie do banku

    <img src="https://bank.com/transfer?to=attacker&amount=10000" />
  4. Przegladarka automatycznie dołącza ciasteczka

    Zadanie jest wysyłane z ciasteczkiem sesji - dla serwera wyglada jak legitymne.

  5. 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 CSRF
session_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>
<?php
session_start();
// Generowanie tokenu CSRF
function generateCsrfToken(): string {
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
// Weryfikacja tokenu CSRF
function verifyCsrfToken(string $token): bool {
if (empty($_SESSION['csrf_token'])) {
return false;
}
return hash_equals($_SESSION['csrf_token'], $token);
}
// Obsługa formularza
if ($_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 tagu
const csrfToken = document.querySelector('meta[name="csrf-token"]').content;
// Dołączenie tokenu do zadania fetch
async 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();
}
<?php
function verifyCsrfFromHeader(): bool {
$headerToken = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
return verifyCsrfToken($headerToken);
}

Najpopularniejsza metoda - każdy formularz zawiera unikalny, nieprzewidywalny token.

<?php
setcookie('session_id', $sessionId, [
'samesite' => 'Strict', // lub 'Lax'
'httponly' => true,
'secure' => true
]);
WartośćZachowanie
StrictCiasteczko tylko dla żądań z tej samej strony
LaxPozwala na top-level navigation (linki), blokuje POST
NoneBrak ochrony (wymaga Secure)
<?php
function 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:

  1. Tokenow CSRF w formularzach i zadaniach AJAX
  2. SameSite cookies jako dodatkowa warstwa ochrony
  3. Weryfikacji Origin/Referer dla krytycznych operacji
  4. Rozroznienia od XSS - to różne ataki!

Pamiętaj: Samo logowanie nie chroni przed CSRF - właśnie zalogowani użytkownicy sa celem tego ataku.