W epoce powszechnej cyfryzacji zagrożenia dla aplikacji webowych dotykają organizacje i użytkowników na całym świecie. Wśród nich Cross-Site Request Forgery (CSRF), znany także jako XSRF, należy do ataków trudnych do wykrycia, a zarazem potencjalnie bardzo destrukcyjnych. Atak CSRF zmusza przeglądarkę zalogowanego użytkownika do wysłania spreparowanego żądania bez jego wiedzy, wykorzystując zaufanie serwera do przeglądarki. Konsekwencje sięgają od zmiany hasła i transferów finansowych po modyfikacje uprawnień i przejęcie konta.

Treść (pokaż)

Fundamentalne pojęcia i definicja ataku CSRF

Czym jest atak CSRF (cross-site request forgery)?

Cross-Site Request Forgery według OWASP to atak, w którym zalogowana ofiara zostaje skłoniona do wysłania żądania HTTP do podatnej aplikacji, a przeglądarka automatycznie dołącza ciasteczko sesji i inne dane uwierzytelniające. Z perspektywy serwera takie żądanie wygląda jak legalne działanie zaufanego użytkownika.

CSRF nie wymaga instalacji złośliwego oprogramowania ani modyfikacji przeglądarki. Wykorzystuje naturalną cechę webu: automatyczne wysyłanie cookies do danej domeny przy każdym żądaniu.

Historyczne znaczenie i pozycja w rankingach zagrożeń

CSRF zajmował pozycję 5. w OWASP Top 10 (2010), spadając do 8. w 2013 roku dzięki rosnącej świadomości i domyślnym zabezpieczeniom w popularnych frameworkach. Mimo spadku w rankingach, atak nadal zagraża bankowości, mediom społecznościowym i e-commerce.

Mechanika i sposób działania ataków CSRF

Fundamentalne wymagania dla sukcesu ataku

Aby CSRF się powiódł, zwykle muszą być spełnione następujące warunki:

  • istotna akcja – atakujący ma motywację do jej wywołania (np. zmiana hasła, przelew, podniesienie uprawnień);
  • uwierzytelnianie wyłącznie ciasteczkiem – aplikacja polega na cookies sesji bez dodatkowej weryfikacji pochodzenia żądania;
  • brak nieprzewidywalnych parametrów – brak wartości, których atakujący nie zna (np. konieczność podania starego hasła).

Automatyczne wysyłanie ciasteczek – kluczowy element ataku

Gdy przeglądarka wysyła żądanie do danej domeny, automatycznie dołącza zapisane dla niej cookies. Dzieje się to bez udziału użytkownika i bez konieczności użycia JavaScript. Ta wygodna cecha webu bywa jednocześnie wektorem dla CSRF.

Fazy ataku CSRF

Typowy atak CSRF przebiega w pięciu krokach:

  1. ofiara loguje się do podatnej aplikacji i otrzymuje ciasteczko sesji;
  2. pozostając zalogowaną, odwiedza stronę kontrolowaną przez atakującego;
  3. strona atakującego generuje żądanie do aplikacji ofiary (GET/POST itp.);
  4. przeglądarka automatycznie dołącza cookies sesyjne do żądania;
  5. serwer uznaje żądanie za autoryzowane i wykonuje akcję.

Wektory ataku – metody dostarczania ładunku CSRF

Najczęściej spotykane techniki dostarczenia ładunku to:

  • tag HTML <img> – adres obrazka zawiera żądanie GET, np. <img src="http://bank.com/transfer.do?recipient=attacker&amount=1000000">;
  • automatycznie wysyłający się formularz POST – ukryte pola + wysłanie w onload, np. <form action="http://bank.com/transfer.do" method="POST">...</form>;
  • XHR/Fetch – ograniczane przez Same-Origin Policy; odpowiedzi cross-origin są blokowane bez właściwej konfiguracji CORS.

Rzeczywiste scenariusze ataków CSRF

Przykład pierwszy – panel administracyjny forum dyskusyjnego

Atak polegający na utworzeniu konta admina przez wstrzyknięty tag <img> w danych profilu. Gdy administrator przegląda listę użytkowników, przeglądarka ładuje obraz, wywołując żądanie do panelu i tworzy konto z uprawnieniami administracyjnymi.

Przykład drugi – zmiana hasła na portalu

Na podatnym portalu możliwa jest cicha zmiana hasła przez kliknięcie spreparowanego linku (często po uprzednim skróceniu URL), jeśli proces zmiany używa parametrów w adresie i brak jest ochrony CSRF.

Przykład trzeci – nieautoryzowany przelew bankowy

Atakujący osadza <img> lub formularz POST realizujący żądanie przelewu. Bank uznaje żądanie jako legalne dzięki ważnemu cookie sesji i wykonuje transakcję, o ile nie istnieje dodatkowa weryfikacja (np. SMS/OTP).

Różne rodzaje ataków CSRF

Przechowywane ataki CSRF (stored CSRF)

Atakujący umieszcza kod ataku bezpośrednio w podatnej aplikacji (np. w komentarzu jako <img> lub <iframe>). Wystarczy samo odwiedzenie strony przez zalogowanego użytkownika, aby atak się wykonał.

Login CSRF

Ofiara zostaje nieświadomie zalogowana na konto kontrolowane przez atakującego. Konsekwencje obejmują śledzenie aktywności, mylące konteksty i możliwość wyłudzeń.

Obrona przed atakami CSRF – strategia wielowarstwowa

Tokeny anty-CSRF – najskuteczniejsza linia obrony

Tokeny anty-CSRF (synchronizer tokens) to złoty standard: serwer generuje unikalny, losowy, nieprzewidywalny token i osadza go w formularzach lub żądaniach modyfikujących stan. Żądanie bez poprawnego tokenu jest odrzucane.

Generowanie i weryfikacja tokenów

Bezpieczny token powinien spełniać te kryteria:

  • unikalność – inny token dla każdej sesji (a najlepiej także dla każdego żądania);
  • tajność – niewidoczny i nieodczytywalny dla atakującego;
  • nieprzewidywalność – odpowiednia entropia (np. co najmniej 128 bitów losowości).

Przykład w PHP (generowanie tokenu): $_SESSION['token'] = bin2hex(random_bytes(24));. Weryfikacja powinna używać bezpiecznego porównania (np. hash_equals()) i skutkować błędem 403, gdy tokenu brak lub jest nieprawidłowy.

Synchronizer Token Pattern

Serwer przechowuje token w sesji i osadza go w formularzach/żądaniach. Przy wysyłce żądania token z formularza musi zgadzać się z tokenem w sesji. Zaletą jest wysoka skuteczność; wadą – konieczność przechowywania stanu po stronie serwera.

Double Submit Cookies

Ten sam token przesyłany jest zarówno w ciasteczku, jak i w parametrze żądania. Serwer sprawdza ich zgodność, co pozwala na walidację bez stanu. Słabością bywa możliwość „wstrzyknięcia” ciasteczka przez atakującego.

Bardziej bezpieczny wariant to Signed Double-Submit Cookie, gdzie token jest powiązany z sesją i podpisany kryptograficznie:

secret = getSecretSecurely("CSRF_SECRET")
sessionID = session.sessionID
randomValue = cryptographic.randomValue(64)
message = sessionID.length + "!" + sessionID + "!" + randomValue.length + "!" + randomValue.toHex()
hmac = hmac("SHA256", secret, message)
csrfToken = hmac.toHex() + "." + randomValue.toHex()

Podczas walidacji serwer ponownie oblicza HMAC i porównuje go z przekazanym tokenem.

Atrybut SameSite cookies – nowoczesna linia obrony

SameSite kontroluje, czy ciasteczka mają być wysyłane w kontekście cross-site. To dodatkowa warstwa, która nie zastępuje tokenów CSRF. Poniżej syntetyczne porównanie trybów:

Tryb Kiedy cookie jest wysyłane Zalety Wady Typowe zastosowanie
Strict wyłącznie same-site najwyższa ochrona przed CSRF może psuć UX przy przejściach z linków panele administracyjne, krytyczne zasoby
Lax nawigacje GET (np. kliknięcia linków), nie dla POST cross-site dobry kompromis bezpieczeństwo/UX nie chroni wszystkich przepływów większość sesji użytkowników
None w każdym kontekście cross-site (wymaga Secure) wspiera integracje międzydomenowe najmniej restrykcyjny usługi wymagające third-party cookies

Walidacja nagłówka Referer i Origin

W praktyce lepiej opierać się na Origin niż na Referer:

  • Origin zawiera jedynie schemat, host i port – jest bardziej restrykcyjny;
  • jest nagłówkiem zabronionym do modyfikacji przez JavaScript – trudniejszy do sfałszowania;
  • Referer bywa ukrywany ze względów prywatności i może być podatny na manipulacje.

Fetch Metadata – nowoczesna obrona na poziomie przeglądarki

Przeglądarki dodają nagłówki Fetch Metadata (np. Sec-Fetch-Site), dzięki którym serwer może odrzucać żądania cross-site dla wrażliwych ścieżek:

app.post("/transfer", (req, res) => {
const secFetchSite = req.headers["sec-fetch-site"];
if (secFetchSite === "same-origin" || secFetchSite === "same-site") {
// process request
} else {
// reject
}
});

Wymaganie ponownej autentykacji dla wrażliwych operacji

Żądaj ponownego uwierzytelnienia (hasło, SMS, TOTP) przed zmianą hasła, dużym przelewem czy modyfikacją uprawnień. Nawet skuteczne CSRF nie przejdzie tej bramki bez świadomego działania użytkownika.

Obrona użytkowników – praktyczne kroki minimalizujące ryzyko

Dodatkowo użytkownicy mogą ograniczyć skutki CSRF:

  • regularne wylogowywanie – brak aktywnej sesji uniemożliwia atakowi działanie;
  • ostrożność wobec linków/obrazków – nie klikać podejrzanych adresów z e-maili i social mediów;
  • włączenie 2FA – kody SMS/OTP blokują krytyczne operacje nawet przy CSRF;
  • unikanie „zapamiętaj mnie” i rozsądne czasy wygaśnięcia sesji.

Różnice między CSRF a innymi atakami na bezpieczeństwo

CSRF vs. cross-site scripting (XSS)

Kluczowe różnice:

  • zakres możliwości – CSRF inicjuje żądania bez dostępu do odpowiedzi, XSS pozwala na wykonanie dowolnego JS i odczyt danych;
  • polityka Same-Origin – ogranicza CSRF w odczycie, XSS „dziedziczy” zaufanie origin i omija te bariery;
  • relacja z tokenami CSRF – tokeny utrudniają CSRF, lecz XSS może je wykraść i użyć.

CSRF vs. SQL injection

SQL injection atakuje serwer/bazę przez wstrzyknięcie zapytań, a obrona to m.in. prepared statements. CSRF atakuje przeglądarkę użytkownika, a obrona to tokeny, SameSite, Origin/Referer i Fetch Metadata.

Najlepsze praktyki dla deweloperów

Korzystanie z frameworków z wbudowaną ochroną CSRF

Stosuj nowoczesne frameworki (Django, Ruby on Rails, Laravel, ASP.NET Core, Spring), które domyślnie włączają ochronę CSRF i regularnie ją aktualizują.

Implementacja tokenów CSRF w aplikacjach

Przykład (Laravel):

<form method="POST" action="/profile">
@csrf
<!-- form fields -->
</form>

Po stronie serwera middleware (np. ValidateCsrfToken) weryfikuje token przed przetwarzaniem żądania.

Walidacja po stronie serwera

Waliduj tokeny wyłącznie na serwerze. Logika po stronie klienta jest łatwa do obejścia.

Unikanie podatnych wzorców

Wyeliminuj następujące błędy projektowe:

  • operacje zmieniające stan przez GET – GET tylko do odczytu, bez skutków ubocznych;
  • poleganie wyłącznie na Referer – niestabilny i możliwy do ukrycia lub manipulacji;
  • przechowywanie tokenu CSRF wyłącznie w cookie – cookie i tak zostanie dołączone cross-site.

Konfiguracja CORS

Konfiguruj CORS restrykcyjnie, wskazując zaufane domeny; unikaj Access-Control-Allow-Origin: * dla zasobów uwierzytelnionych.

Detekcja i testowanie podatności CSRF

Ręczna analiza kodu

Identyfikuj wszystkie żądania modyfikujące stan (POST/PUT/PATCH/DELETE i niefortunne GET) i sprawdzaj, czy chroni je ważny token CSRF oraz czy nie ujawniają wrażliwych parametrów w URL.

Testowanie manualne

Praktyczna procedura testu wygląda następująco:

  1. przechwyć prawidłowe żądanie chronione tokenem;
  2. usuń token lub podmień go na nieprawidłowy;
  3. wyślij ponownie żądanie i obserwuj odpowiedź serwera;
  4. pozytywny wynik testu to odrzucenie z błędem (np. 403).

Zautomatyzowane skanowanie

Narzędzia DAST automatyzują wyszukiwanie brakujących lub słabych zabezpieczeń CSRF. Stanowią uzupełnienie testów ręcznych i przeglądu kodu.