DOM-based XSS
| Po angielsku | DOM-based XSS |
|---|---|
| Którą formą pisać | po angielsku Polski odpowiednik istnieje, ale w praktyce nikt tak nie pisze. Zostaw angielski, polski trzymaj jako synonim. |
| Inne formy | DOM XSS · DOM Based XSS · XSS oparty na DOM |
| Kategoria | Podatności i klasy błędów |
DOM-based XSS to odmiana XSS (Cross-Site Scripting), w której ładunek wykonuje się dlatego, że skrypt strony zmienia środowisko DOM w przeglądarce ofiary, a sama odpowiedź HTTP serwera pozostaje bez zmian [2]. PortSwigger opisuje ten błąd jako sytuację, w której JavaScript pobiera dane ze źródła kontrolowanego przez atakującego, na przykład z adresu URL, i przekazuje je do miejsca pozwalającego na dynamiczne wykonanie kodu [1]. MITRE nazywa tę odmianę typem 0 XSS [4]. W polskich tekstach Sekurak opisuje DOM Based XSS jako rzadziej spotykaną postać tej podatności [8].
Jak działa
Mechanizm opisuje się parą pojęć: źródło (source) i ujście (sink). Źródło to miejsce, z którego skrypt czyta dane, najczęściej adres strony dostępny przez window.location [1]. Ujście to funkcja albo właściwość, która traktuje przekazany tekst jak HTML albo kod. PortSwigger wymienia wśród groźnych ujść document.write(), innerHTML, outerHTML, insertAdjacentHTML i atrybuty obsługi zdarzeń [1]. Podatność powstaje, gdy dane płyną od źródła do ujścia bez neutralizacji.
Szczególną rolę odgrywa fragment adresu po znaku #. OWASP zwraca uwagę, że przeglądarka nie wysyła tej części adresu do serwera [2]. Ładunek umieszczony we fragmencie nie trafia więc do logów serwera ani do filtrów działających po jego stronie. Schemat poniżej pokazuje przebieg takiego ataku.
Różnicę między ujściem groźnym i bezpiecznym widać na przykładzie. Pierwsza linia interpretuje tekst jako HTML, druga wstawia go jako zwykły tekst.
const opis = decodeURIComponent(location.hash.slice(1));
el.innerHTML = opis; // podatne: tekst jest parsowany jako HTML
el.textContent = opis; // bezpieczne: tekst pozostaje tekstem
Odmiany
DOM-based XSS różni się od dwóch pozostałych typów miejscem, w którym ładunek zostaje złożony w stronę. W odmianach odbitej i trwałej ładunek trafia do odpowiedzi z powodu błędu po stronie serwera; w DOM-based XSS składa go kod działający w przeglądarce [2].
| Cecha | Reflected XSS | Stored XSS | DOM-based XSS |
|---|---|---|---|
| gdzie powstaje błąd | kod serwera [4] | kod serwera [4] | skrypt w przeglądarce [4] |
| czy odpowiedź zawiera ładunek | tak | tak | nie musi [2] |
| widoczność w logach serwera | tak | tak | nie, gdy ładunek jest po # [2] |
Szczególnym przypadkiem są biblioteki JavaScript. PortSwigger wskazuje funkcje jQuery, takie jak html(), append(), attr() i selektor $(), jako możliwe ujścia; selektor bywa groźny w połączeniu z location.hash jako źródłem [1].
W WordPressie
W WordPressie DOM-based XSS dotyczy przede wszystkim skryptów JavaScript dołączanych przez wtyczki i motywy, zwłaszcza kreatory stron, galerie i okna typu lightbox. Jest to więc odmiana podatności we wtyczce albo podatności w motywie; funkcje PHP chroniące wyjście HTML nie obejmują danych, które skrypt przegląda już w przeglądarce.
Przykładem jest wtyczka Elementor. WPScan opisał błąd CVE-2021-24891: wtyczka nie czyściła ani nie kodowała danych wstawianych do DOM ze spreparowanego fragmentu adresu, co dawało DOM-based XSS [6]. Poprawka ukazała się w wersji 3.4.8 [6]. WPScan odnotował, że błąd usunięto już w wersji 3.1.4, ale wrócił w wersji 3.2.0 [6]. To przykład regresji: ta sama ścieżka od źródła do ujścia pojawiła się ponownie po zmianach w kodzie.
Po stronie PHP dokumentacja WordPressa zaleca kodowanie danych dla kontekstu, w tym esc_js() dla JavaScriptu w linii [7]. Chroni to dane wypisywane przez serwer do skryptu. Nie zastępuje jednak ostrożności w samym skrypcie, który później wstawia te dane do strony.
Zagrożenia i skutki
Skutki są takie jak w innych odmianach XSS (Cross-Site Scripting): skrypt atakującego działa z uprawnieniami witryny. MITRE wymienia ujawnienie danych sesji i ciasteczek oraz manipulację nimi [4]. W praktyce oznacza to ryzyko przejęcia sesji, działań w panelu w imieniu zalogowanego administratora i podmiany treści strony.
Utrudnieniem dla obrońców jest to, że atak może nie zostawić śladu w logach serwera [2]. Zapora aplikacyjna WAF, która analizuje żądania HTTP, nie widzi fragmentu adresu, bo przeglądarka go nie wysyła [2].
Jak się bronić
OWASP formułuje zasadę ogólną: niezaufane dane należy traktować wyłącznie jako tekst do wyświetlenia [3]. Najbardziej podstawowym bezpiecznym sposobem wstawienia takich danych do DOM jest właściwość textContent [3]. Przy setAttribute wolno używać tylko atrybutów, które nie wykonują kodu; ściąga OWASP wyklucza atrybuty obsługi zdarzeń [3]. Należy unikać innerHTML, outerHTML, document.write() oraz funkcji, które niejawnie wykonują przekazany tekst, na przykład setTimeout z argumentem tekstowym [3].
Drugą warstwą jest Trusted Types. MDN opisuje ten mechanizm jako sposób na wymuszenie, by dane przeszły przez funkcję przekształcającą wskazaną przez programistę, zanim trafią do API zdolnego je wykonać [5]. Włącza go dyrektywa require-trusted-types-for w CSP (Content Security Policy); przekazanie zwykłego tekstu do ujścia kończy się wtedy wyjątkiem TypeError [5]. Według MDN mechanizm działa w najnowszych wersjach głównych przeglądarek od lutego 2026 roku [5].
- Przegląd kodu JavaScript wtyczek i motywów pod kątem przepływu od
locationdo ujść HTML. - Sanityzacja danych, gdy HTML jest naprawdę potrzebny, przez sprawdzoną bibliotekę.
- Aktualizacja wtyczek, bo poprawki tej klasy błędów wydają producenci.
Historia i przykłady
Pojęcie opisał Amit Klein w lipcu 2005 roku w tekście „DOM Based Cross Site Scripting or XSS of the Third Kind” [2]. Wcześniej rozróżniano tylko XSS odbity i trwały; nowa kategoria objęła błędy, w których serwer nie uczestniczy w osadzaniu ładunku [2].
Przypisy
8- [1] DOM-based XSS · PortSwigger Web Security Academy
- [2] DOM Based XSS · OWASP
- [3] DOM based XSS Prevention Cheat Sheet · OWASP Cheat Sheet Series
- [4] CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') · MITRE
- [5] Trusted Types API · MDN Web Docs
- [6] Elementor < 3.4.8 – DOM Cross-Site-Scripting (CVE-2021-24891) · WPScan
- [7] Escaping Data — Common APIs Handbook · WordPress.org
- [8] Czym jest XSS? · Sekurak