Jak działają tokeny CSRF – generowanie, przesyłanie i ochrona aplikacji webowej
Czym jest atak CSRF?
Cross-Site Request Forgery (CSRF) to rodzaj ataku na aplikacje webowe, w którym złośliwa strona nakłania zalogowanego użytkownika do wykonania nieautoryzowanej akcji w innej aplikacji, w której użytkownik jest już uwierzytelniony. Przykładem może być zmiana hasła, przesłanie formularza lub wykonanie przelewu — wszystko bez wiedzy użytkownika.
Rola tokenów CSRF w ochronie
Aby zabezpieczyć się przed atakami CSRF, aplikacje wykorzystują tzw. tokeny CSRF (ang. CSRF tokens). Są to losowo generowane ciągi znaków, które mają na celu upewnienie się, że dane żądanie pochodzi od autentycznego użytkownika i zostało wysłane z legalnego źródła (czyli samej aplikacji).
Jak działają tokeny CSRF?
Proces działania tokenów CSRF można opisać w kilku krokach:
1. Generowanie tokena
Podczas ładowania strony (np. formularza), serwer generuje unikalny, trudny do przewidzenia token CSRF. Cechy typowego tokena:
- Jest losowy i trudny do odgadnięcia (np. 128-bitowy ciąg zakodowany w base64).
- Jest związany z sesją użytkownika (przechowywany w sesji po stronie serwera lub w pamięci użytkownika).
Przykład (PHP):
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
2. Umieszczenie tokena w formularzu
Token CSRF jest dodawany do formularza HTML jako ukryte pole:
<input type="hidden" name="csrf_token" value="...">
Można go także dodać do nagłówka AJAX, jeśli formularze są przesyłane dynamicznie (np. za pomocą JavaScript).
3. Przesłanie tokena z żądaniem
Kiedy użytkownik przesyła formularz, token CSRF jest przesyłany razem z danymi żądania (np. jako część POST). Serwer odbiera token i porównuje go z wartością zapisaną w sesji:
if ($_POST['csrf_token'] !== $_SESSION['csrf_token']) {
die("Błąd: nieprawidłowy token CSRF.");
}
Jeśli token się nie zgadza lub go brakuje — żądanie jest odrzucane.
Sposoby przesyłania tokenów CSRF
Tokeny mogą być przesyłane na kilka sposobów:
- Jako ukryte pole w formularzu HTML – najczęstsza metoda.
- W nagłówku HTTP (np.
X-CSRF-Token
) – stosowane przy AJAX. - Jako parametr w URL – rzadziej stosowane z uwagi na bezpieczeństwo (token widoczny w historii przeglądarki, logach serwera itp.).
Dlaczego to działa?
Atakujący nie ma dostępu do wartości tokena CSRF, ponieważ nie ma możliwości odczytania zawartości formularza generowanego przez aplikację (np. z innej domeny). Nawet jeśli wymusi wysłanie formularza, nie zna poprawnego tokena — więc serwer odrzuci żądanie.
Dobre praktyki
- Generuj nowe tokeny dla każdej sesji lub formularza.
- Tokeny powinny być długie i losowe.
- Weryfikuj token przy każdym istotnym żądaniu zmieniającym dane.
- Stosuj
SameSite
dla ciasteczek (SameSite=Lax
lubStrict
) jako dodatkowe zabezpieczenie.
Przykład użycia CSRF w Koseven Framework (na bazie sesji)
Oto prosty sposób krok po kroku działający na bazie sesji.
1. Generowanie tokena CSRF i zapis w sesji
W kontrolerze, w metodzie odpowiedzialnej za wyświetlenie formularza:
public function action_form()
{
$session = Session::instance();
// Generowanie tokena jeśli nie istnieje
if (!$session->get('csrf_token')) {
$token = bin2hex(random_bytes(32));
$session->set('csrf_token', $token);
} else {
$token = $session->get('csrf_token');
}
$view = View::factory('form_view');
$view->csrf_token = $token;
$this->response->body($view);
}
2. Dodanie tokena do formularza (widok)
W pliku views/form_view.php
:
<form action="/example/submit" method="post">
<input type="hidden" name="csrf_token" value="<?= HTML::chars($csrf_token) ?>">
<!-- inne pola formularza -->
<input type="submit" value="Wyślij">
</form>
3. Weryfikacja tokena przy odbiorze danych
W kontrolerze, w metodzie obsługującej POST
:
public function action_submit()
{
$session_token = Session::instance()->get('csrf_token');
$post_token = $this->request->post('csrf_token');
if (!$post_token || $post_token !== $session_token) {
throw new HTTP_Exception_403('Nieprawidłowy token CSRF');
}
// Jeśli token się zgadza, można przetwarzać dane
// ... dalsza logika przetwarzania formularza ...
$this->response->body("Dane zostały przesłane poprawnie.");
}
4. (Opcjonalnie) Resetowanie tokena po przesłaniu
Aby zapobiec wielokrotnemu użyciu tego samego tokena (ang. replay attack), warto usunąć go po jednorazowym użyciu:
Session::instance()->delete('csrf_token');
Przykład użycia CSRF w Koseven Framework (z wykorzystaniem Security::token()
)
Framework Koseven dostarcza także prosty sposób ochrony przed atakami CSRF za pomocą wbudowanej klasy Security
. Funkcja Security::token()
automatycznie generuje token CSRF, przechowuje go w sesji i umożliwia weryfikację przez Security::check()
.
1. Generowanie tokena i przekazanie do widoku
W kontrolerze odpowiedzialnym za formularz:
public function action_form()
{
$csrf_token = Security::token();
$view = View::factory('form_view')
->set('csrf_token', $csrf_token);
$this->response->body($view);
}
2. Umieszczenie tokena w formularzu (widok)
W pliku views/form_view.php
:
<form action="/example/submit" method="post">
<input type="hidden" name="csrf" value="<?= HTML::chars($csrf_token) ?>">
<!-- inne pola formularza -->
<input type="submit" value="Wyślij">
</form>
Uwaga: domyślna nazwa pola to
csrf
, chyba że została zmieniona w konfiguracji.
3. Weryfikacja tokena po stronie serwera
W metodzie kontrolera przetwarzającej dane POST:
public function action_submit()
{
if (!Security::check($this->request->post('csrf'))) {
throw new HTTP_Exception_403('Nieprawidłowy token CSRF.');
}
// Token poprawny – przetwarzamy dane
$this->response->body("Dane zostały przesłane poprawnie.");
}
Dodatkowe informacje
Security::token()
automatycznie generuje nowy token, jeśli nie istnieje.- Token jest domyślnie przechowywany w sesji (
$_SESSION['security_token']
). - Weryfikacja przez
Security::check()
sprawdza poprawność.
Podsumowanie
Tokeny CSRF to jedna z najskuteczniejszych metod ochrony przed fałszywymi żądaniami wysyłanymi w kontekście uwierzytelnionego użytkownika. Poprzez generowanie unikalnego tokena i jego weryfikację przy każdym żądaniu, aplikacja może z dużą skutecznością zapobiegać atakom typu CSRF.
Koseven ułatwia ochronę przed atakami CSRF, oferując gotowe metody Security::token()
i Security::check()
. Dzięki temu: