Cross-Site Scripting powstaje, gdy dane przeznaczone do wyświetlenia stają się wykonywalną treścią przeglądarki. Payload może pochodzić z formularza, URL, bazy, API lub zależności. Output encoding jest mocną obroną tylko wtedy, gdy odpowiada miejscu docelowemu. HTML text, attribute, JavaScript, CSS i URL mają różne gramatyki.
Browser interpretuje kilka języków naraz
Dokument zawiera markup, scripts, styles i navigation targets. Wartość bezpieczna między tagami może zamknąć string JavaScript. Poprawnie quoted href może nadal zawierać javascript:.
Framework auto-escaping chroni zwykłą interpolację. Raw HTML, innerHTML, event attributes i concatenation omijają tę warstwę.
Escape należy wykonać przy końcowym output
Zapisywanie HTML-encoded inputu miesza dane z prezentacją i powoduje double encoding w JSON, CSV lub mailu. Storage powinien zachować semantyczną wartość.
Renderer zna context i wybiera właściwy encoder. Raw output jest wyjątkiem dla jawnie trusted sanitized markup.
Niebezpiecznych kontekstów często można uniknąć
Untrusted data nie musi trafiać do inline script, style i event handler. JSON, bezpieczne data attributes oraz structured DOM APIs utrzymują kod oddzielnie. Dla text należy używać textContent.
Architektura bez przejścia przez parser jest pewniejsza niż skomplikowane próby neutralizacji każdej składni.
Dozwolony rich text wymaga sanitizera
CMS lub komentarz może obsługiwać paragraphs i links. Sanitizer parsuje HTML oraz wymusza allowlist tags, attributes i schemes. Powinien rozumieć malformed markup tak jak browser.
String replacement i regex nie pokrywają nesting, SVG i error recovery.
URL wymaga policy oprócz attribute encoding
Escaping quote utrzymuje wartość w atrybucie, ale nie czyni celu bezpiecznym. Aplikacja parsuje scheme, host albo wewnętrzny path. Open redirect może mieć idealną składnię.
Najpierw kontroluje się znaczenie URL, potem koduje je do atrybutu. To dwie osobne bariery.
CSP ogranicza skutki
Content Security Policy może blokować nieoczekiwane scripts, wymagać nonce i raportować naruszenia. Pomaga również wykryć ukryte inline code podczas migracji.
CSP jest defense in depth. unsafe-inline i szerokie allowlisty osłabiają kontrolę. Encoding pozostaje obowiązkowy.
Client code może ponownie wprowadzić XSS
Serwer bezpiecznie renderuje query, lecz JavaScript odczytuje je później i przypisuje do innerHTML. DOM-based XSS powstaje bez wadliwej odpowiedzi backendu.
Review musi obejmować sinks po initial render. Third-party widgets potrzebują oceny, izolacji i aktualizacji.
Stored XSS czeka na uprzywilejowanego użytkownika
Payload w profilu lub tickecie może wykonać się miesiące później w panelu admina. Dane zapisane nie stają się trusted. Każdy renderer stosuje właściwy context.
Wewnętrzne narzędzia są szczególnie wrażliwe, bo ich użytkownicy mają większe uprawnienia. Dashboard security również wyświetla payload jako text.
Migracja frameworka wymaga audytu sinks
Template engines mają różne defaults i raw helpers. Zmiana może przekształcić pole text w oczekiwany HTML albo odwrotnie. Przed migracją należy znaleźć raw output, sanitizers i DOM sinks.
Regression tests z realnymi payloadami chronią zarówno przed XSS, jak i double encoding.
Trusted Types może wzmocnić duży frontend
Trusted Types ogranicza niebezpieczne sinks do wartości utworzonych przez zatwierdzone policies. Przypadkowe przekazanie stringa do innerHTML staje się błędem.
Nie zastępuje sanitization, ale pomaga utrzymać architekturę po wielu zmianach zespołu.
Telemetryka bezpieczeństwa również potrzebuje escaping
CSP reports i rejected payloads są hostile data. Dashboard renderujący je raw tworzy następny XSS podczas incident response.
Metriki mogą zapisywać sink, regułę i request ID bez kopiowania pełnej treści. Forensics wymaga ograniczonego dostępu.
Test w browserze sprawdza prawdziwą interpretację
Assertion szukający <script> nie wykrywa złamanego atrybutu, SVG i DOM mutation. Suite łączy unit tests encoderów z browser tests obserwującymi wykonanie oraz DOM.
Payload z incydentu powinien zostać trwałą fixture. Dokumentuje dokładną naruszoną granicę.
Incident response naprawia granicę, nie tylko rekord
Usunięcie jednego payloadu nie wystarcza. Należy poprawić sink, znaleźć podobne miejsca i ocenić sesje lub credentials użytkowników, którzy widzieli stored XSS.
Historycznych danych nie zawsze trzeba zmieniać, jeśli poprawny renderer bezpiecznie pokazuje je jako text. Migracja i forensics są osobnymi decyzjami.
Stored XSS może dotknąć kanałów poza stroną
Ten sam opis może być renderowany w admin panelu, e-mailu HTML, podglądzie PDF i narzędziu supportu. Naprawa jednej strony nie usuwa pozostałych sinks. Właściciel pola powinien zinwentaryzować wszystkich konsumentów i ich konteksty.
Wyciek może obejmować działania wykonane z uprawnieniami administratora, nie tylko odczyt cookie. Audit zmian konfiguracji i nowych credentials pomaga ocenić rzeczywisty zakres incydentu.
CSP warto wdrażać etapami i obserwować
Tryb report-only pokazuje legalne inline scripts i nieoczekiwane źródła przed włączeniem blokady. Następnie nonce lub hashes pozwalają usunąć szerokie wyjątki. Raporty powinny być grupowane, aby pojedynczy payload nie zalał systemu.
Dodanie unsafe-inline jako szybkiej naprawy może zniszczyć główną wartość policy. Zmiany CSP zasługują na review tak samo jak kod renderujący.
Sanitizer jest zależnością wymagającą aktualizacji
Browser parsing i techniki obejścia ewoluują. Stara biblioteka może nie rozumieć nowych elementów lub namespace. Wersję sanitizera należy śledzić, testować podczas upgrade i mieć fixtures z dopuszczonym oraz zabronionym markup.
Zmiana policy może wymagać ponownej sanitization istniejących danych albo bezpiecznego sanitizing on render. Wybór zależy od audytu i wydajności.
Dane muszą pozostać oddzielone od kodu
Kontekstowe encoding działa, ponieważ zachowuje tę granicę. Sanitization jest kontrolowanym wyjątkiem dla markup, a CSP kolejną warstwą.
Najważniejsze nie jest zapamiętanie jednej funkcji, lecz projekt używający bezpiecznych typów i APIs aż do dokładnie znanego output context.