XSS w aplikacjach Python - rodzaje, skutki i ochrona

Jeremi Andrzejewski 24 sierpnia 2026
Schemat ataku XSS: atakujący wysyła link ze skryptem, ofiara klika, przeglądarka wykonuje złośliwy skrypt, wysyłając dane.

Spis treści

Jedna niepozorna wartość z formularza, komentarza albo adresu URL może sprawić, że przeglądarka wykona kod pochodzący od atakującego. Atak XSS polega właśnie na wykorzystaniu aplikacji internetowej do wstrzyknięcia treści, która zostanie potraktowana jak zaufany kod, dlatego pokazuję tu jego rodzaje, skutki oraz praktyczne sposoby ochrony, także w aplikacjach tworzonych w Pythonie.

Najważniejsze informacje o podatnościach XSS w jednym miejscu

  • XSS pozwala uruchomić złośliwy kod w przeglądarce użytkownika z użyciem zaufanej witryny.
  • Najczęściej wyróżnia się odmianę odbitą, składowaną i DOM-based.
  • Podstawą ochrony jest kodowanie danych zależnie od kontekstu, a nie proste filtrowanie znaków.
  • Atrybuty ciasteczek HttpOnly, Secure i SameSite ograniczają skutki kradzieży sesji.
  • Polityka Content Security Policy działa jako dodatkowa warstwa, ale nie zastępuje poprawnego kodowania wyjścia.

Na czym polega XSS i dlaczego przeglądarka ufa obcej treści

Cross-site scripting, czyli XSS, to luka występująca wtedy, gdy aplikacja pobiera dane kontrolowane przez użytkownika i umieszcza je w stronie w sposób pozwalający przeglądarce potraktować je jako HTML albo JavaScript. Problem nie polega więc wyłącznie na samym skrypcie. Sednem jest pomylenie danych z kodem.

Przeglądarka ufa stronie, którą właśnie otworzyliśmy. Jeżeli podatna aplikacja wyświetla niebezpieczną treść w ramach własnej domeny, kod może działać z uprawnieniami tej witryny. W zależności od sytuacji pozwala to na modyfikowanie interfejsu, wykonywanie żądań w imieniu ofiary albo przechwytywanie danych dostępnych dla kodu strony.

Skutki zależą od miejsca wystąpienia podatności. Publiczny komentarz w serwisie może zaatakować wielu użytkowników, a pojedynczy parametr w panelu administracyjnym może zagrozić tylko osobom mającym dostęp do konkretnej funkcji. Dlatego sama etykieta „XSS” nie mówi jeszcze, jak poważny jest problem.

Co może osiągnąć napastnik

  • podmienić fragment widoku i wyświetlić fałszywy formularz logowania,
  • wykonać działania w aplikacji w imieniu zalogowanej osoby,
  • odczytać dane obecne w stronie lub w pamięci aplikacji JavaScript,
  • przekierować użytkownika do spreparowanej treści,
  • wykorzystać zaufaną domenę do dostarczenia kolejnego etapu oszustwa.

XSS nie jest tym samym co SQL injection. SQL injection uderza przede wszystkim w zapytania kierowane do bazy danych, natomiast XSS wykorzystuje sposób, w jaki aplikacja generuje stronę i jak przeglądarka interpretuje jej zawartość. Obie luki mogą wynikać z niewłaściwego obchodzenia się z danymi wejściowymi, ale wymagają innych zabezpieczeń.

Abstrakcyjna tarcza w odcieniach niebieskiego i zieleni, symbolizująca ochronę przed atakiem XSS.

Trzy główne rodzaje podatności XSS

Podział na rodzaje pomaga szybko ustalić, gdzie szukać błędu i kto może zostać zaatakowany. W praktyce granice bywają mniej oczywiste, szczególnie w nowoczesnych aplikacjach frontendowych, ale trzy poniższe kategorie porządkują większość przypadków.

Odmiana odbita

W przypadku reflected XSS złośliwa wartość trafia do żądania, a serwer od razu umieszcza ją w odpowiedzi. Klasycznym przykładem jest strona wyników wyszukiwania, która wyświetla wpisaną frazę bez właściwego kodowania. Ofiara musi zwykle otworzyć specjalnie przygotowany link albo przesłany formularz.

Ten wariant często pojawia się w komunikatach błędów, filtrach, parametrach sortowania i stronach logowania. Niebezpieczny adres nie musi wyglądać podejrzanie, zwłaszcza gdy prowadzi do prawdziwej domeny i zawiera tylko jeden zmieniony parametr.

Odmiana składowana

Stored XSS jest bardziej kłopotliwy, ponieważ niebezpieczna treść zostaje zapisana w bazie danych lub innym magazynie. Może to być komentarz, nazwa profilu, opis produktu albo wiadomość na forum. Każdy użytkownik otwierający podatny widok może wtedy uruchomić tę treść.

Z mojego punktu widzenia to właśnie ten rodzaj powinien budzić największą czujność podczas przeglądu aplikacji. Jedno wejście może dotknąć wielu kont, a usunięcie problemu wymaga nie tylko poprawy kodu, lecz także znalezienia i oczyszczenia już zapisanych danych.

DOM-based XSS

W odmianie DOM-based cała operacja może odbywać się po stronie przeglądarki. JavaScript pobiera dane z adresu, fragmentu URL albo innego źródła i przekazuje je do niebezpiecznego miejsca w modelu DOM, czyli strukturze reprezentującej dokument HTML.

Ryzyko rośnie, gdy kod używa właściwości takich jak innerHTML, outerHTML lub funkcji wykonujących tekst jako kod. Bezpieczniejszym wyborem dla zwykłego tekstu jest textContent, ponieważ przeglądarka nie interpretuje jego zawartości jako znaczników.

Rodzaj Gdzie powstaje problem Typowy zasięg
Odbity W odpowiedzi generowanej na podstawie bieżącego żądania Najczęściej pojedyncza ofiara
Składowany W zapisanych danych wyświetlanych innym osobom Wielu użytkowników aplikacji
DOM-based W kodzie JavaScript manipulującym stroną Zależny od konkretnego widoku i źródła danych

Jak wygląda podatny kod w aplikacji Python

Framework nie chroni automatycznie przed każdym scenariuszem. Przykładowo Flask i Jinja2 domyślnie kodują wartości wstawiane do szablonów HTML, ale bezpieczeństwo można łatwo osłabić przez użycie filtra safe, ręczne składanie HTML albo wstawianie danych do kodu JavaScript.

from flask import Flask, request, render_template_string

app = Flask(__name__)

@app.get("/search")
def search():
    phrase = request.args.get("q", "")
    template = "

Wyniki dla: {{ phrase }}

" return render_template_string(template, phrase=phrase)

W tym przykładzie Jinja2 traktuje wartość jako tekst i koduje znaki specjalne. Nie oznacza to jednak, że można bez zastanowienia używać danych w dowolnym miejscu. Kodowanie dla HTML nie jest automatycznie bezpieczne dla atrybutu, CSS ani JavaScriptu.

Ryzykowny wzorzec wygląda na przykład tak:

template = "
{{ phrase|safe }}
"

Filtr safe wyłącza automatyczne kodowanie. Ma sens tylko wtedy, gdy zawartość została wcześniej przepuszczona przez sprawdzony sanitizer, czyli narzędzie oczyszczające dozwolony podzbiór HTML. Samo sprawdzenie, czy tekst zawiera fragment script, nie wystarcza, bo ataki mogą wykorzystywać także atrybuty, nietypowe konteksty i różne sposoby zapisu znaków.

Bezpieczniejszy sposób obsługi treści sformatowanej

Jeżeli użytkownik ma dodawać pogrubienia, linki lub listy, trzeba jasno określić dozwolony format i sanitizować wynik. Dla zwykłych komentarzy najlepsza reguła jest prostsza: przechowuj tekst jako tekst, a formatowanie dodawaj po stronie aplikacji.

W projektach Pythonowych przydają się testy sprawdzające, czy znaki takie jak <, > i cudzysłowy są poprawnie kodowane w każdym widoku. Test powinien obejmować także komunikaty błędów, e-maile HTML, eksporty i panel administracyjny, bo luka często kryje się poza główną ścieżką aplikacji.

Jak skutecznie zabezpieczyć aplikację przed XSS

Koduj dane w miejscu wyświetlania

Najważniejsza zasada brzmi: dane trzeba kodować zgodnie z kontekstem, w którym trafiają do dokumentu. Innego mechanizmu wymaga zwykły tekst HTML, innego wartość atrybutu, adres URL, fragment CSS oraz dane umieszczane w JavaScripcie.

  • HTML powinien otrzymać kodowanie znaków specjalnych,
  • atrybuty muszą być cytowane i odpowiednio kodowane,
  • wartości URL trzeba ograniczać do dozwolonych schematów, na przykład https,
  • danych użytkownika nie należy wklejać bezpośrednio do kodu JavaScript,
  • dla zwykłego tekstu w DOM lepiej użyć textContent niż innerHTML.

OWASP podkreśla, że automatyczne kodowanie wyjścia jest podstawową linią obrony, ale nie rozwiązuje wszystkich przypadków. Szczególnej uwagi wymagają szablony z wyłączonym escapingiem, treści HTML dopuszczone przez użytkownika oraz komponenty frontendowe przetwarzające dane po stronie klienta.

Waliduj dane, ale nie traktuj walidacji jak tarczy

Walidacja po stronie serwera nadal jest potrzebna. Pozwala sprawdzić długość, typ, format i zakres danych, na przykład ograniczyć nazwę użytkownika do liter, cyfr, spacji oraz kilku znaków specjalnych.

To jednak nie zastępuje kodowania wyjścia. Walidacja mówi, co aplikacja akceptuje, a kodowanie mówi przeglądarce, jak tę wartość wyświetlić. Pole opisu produktu może legalnie zawierać znaki HTML, ale nie oznacza to, że wolno umieścić je bezpośrednio w stronie.

Dodaj warstwy ograniczające skutki

Ciasteczka sesyjne powinny mieć ustawione atrybuty HttpOnly i Secure, a w wielu scenariuszach także odpowiednią wartość SameSite. HttpOnly utrudnia odczyt ciasteczka przez JavaScript, Secure wymusza przesyłanie go przez HTTPS, a SameSite ogranicza wysyłanie ciasteczka w żądaniach między witrynami.

Te ustawienia nie usuwają samej luki XSS. Mogą jednak ograniczyć możliwość przejęcia sesji i zmniejszyć skutki błędu. Skrypt uruchomiony w kontekście strony nadal może próbować wykonać działania dostępne dla zalogowanego użytkownika, dlatego nie wolno uznać flag ciasteczek za pełną ochronę.

Dodatkową barierę zapewnia Content Security Policy, czyli nagłówek określający, z jakich źródeł przeglądarka może ładować skrypty i jakie formy wykonania kodu są dozwolone. Najlepiej wdrażać ją etapami, zaczynając od trybu raportowania, ponieważ zbyt restrykcyjna polityka może uszkodzić działające funkcje aplikacji.

Jak testować i naprawiać wykrytą lukę

Testy bezpieczeństwa powinny odbywać się wyłącznie na systemie, do którego mamy zgodę. Na początek warto przejść przez wszystkie miejsca przyjmujące dane od użytkownika i sprawdzić, gdzie te dane wracają: w odpowiedzi HTML, atrybucie, skrypcie, adresie URL albo interfejsie budowanym przez JavaScript.

  1. Zidentyfikuj źródło danych, na przykład parametr żądania, formularz, plik lub rekord z bazy.
  2. Ustal miejsce, w którym wartość jest wyświetlana lub przekazywana dalej.
  3. Sprawdź, czy framework wykonuje automatyczne kodowanie.
  4. Przejrzyj wyjątki, takie jak safe, ręczne składanie HTML i użycie innerHTML.
  5. Dodaj test regresyjny, który potwierdzi, że dane pozostają tekstem.
  6. Usuń lub oczyść wcześniej zapisane wartości, jeśli problem dotyczył odmiany składowanej.

W bezpiecznym środowisku testowym można użyć nieszkodliwego znacznika kontrolnego i obserwować, czy aplikacja zwraca go jako tekst, czy interpretuje jako element dokumentu. Nie skupiałbym się wyłącznie na jednym prostym przykładzie, ponieważ liczy się zachowanie konkretnego kontekstu, a nie reakcja na pojedynczy ciąg znaków.

Do przeglądu aplikacji można wykorzystać skanery DAST, testy manualne i analizę kodu. Skaner dobrze wykrywa powtarzalne problemy, ale nie zawsze rozumie logikę aplikacji, nietypowe przepływy danych ani warunki wymagające zalogowania. Najlepszy efekt daje połączenie automatyzacji z krótkim, celowanym code review.

Przeczytaj również: AWS Fargate - Czy to naprawdę serwerless bez obowiązków?

Typowe błędy podczas naprawy

  • filtrowanie tylko słowa „script”,
  • kodowanie danych przy zapisie zamiast przy wyświetlaniu,
  • wyłączenie ochrony szablonów, bo „inaczej HTML nie działa”,
  • uznanie CSP za zamiennik poprawnego kodowania,
  • zabezpieczenie formularza, ale pominięcie danych importowanych z plików lub API,
  • usunięcie kodu aplikacji bez oczyszczenia zainfekowanych rekordów.

Najczęściej problem nie wynika z braku wiedzy o XSS, tylko z niejasnej odpowiedzialności za dane. Warto ustalić w projekcie, które komponenty mogą renderować HTML, kto je sanitizuje i jakie testy muszą przejść przed wdrożeniem. Jedna centralna funkcja do bezpiecznego renderowania bywa praktyczniejsza niż dziesiątki ręcznych filtrów rozsianych po kodzie.

Jak ograniczyć ryzyko w codziennej pracy nad projektem

Ochrona zaczyna się przed napisaniem pierwszego widoku. W Pythonie dobrze jest pozostawić domyślne mechanizmy escapingowe frameworka, ograniczyć ręczne generowanie HTML i dokumentować każdy przypadek, w którym aplikacja świadomie pozwala na formatowanie treści.

W pipeline CI warto uruchamiać testy jednostkowe, analizę zależności i skanowanie aplikacji. Dobrą praktyką jest także sprawdzanie nagłówków bezpieczeństwa oraz przegląd miejsc, w których frontend korzysta z danych z location, parametrów URL, pamięci lokalnej i komunikatów między oknami.

MDN zwraca uwagę, że przeglądarka wykonuje złośliwą treść tak, jakby należała do zaatakowanej witryny. Dla zespołu oznacza to konkretną rzecz: nie wystarczy chronić serwera, trzeba kontrolować również granicę między danymi a kodem w przeglądarce.

Jeżeli aplikacja przyjmuje HTML od użytkowników, trzeba zaakceptować dodatkowy koszt utrzymania. Potrzebne są reguły dozwolonych znaczników, sanitizacja, testy aktualizacyjne i ostrożność przy zmianie biblioteki. Dla wielu projektów prostszy tekst bez HTML będzie rozsądniejszym i tańszym rozwiązaniem.

Najlepsza kolejność działań po wykryciu XSS

Po potwierdzeniu podatności najpierw ogranicz jej ekspozycję, na przykład wyłącz problematyczną funkcję albo ukryj ją przed użytkownikami. Później popraw sposób kodowania, sprawdź podobne miejsca i przeanalizuj logi pod kątem nietypowych żądań oraz zmian w danych.

Nie warto zatrzymywać się na jednym widoku. Jeżeli błąd powstał przez wspólny komponent, filtr lub wzorzec programistyczny, jego kopie mogą występować w wielu modułach. Naprawa przyczyny jest ważniejsza niż usunięcie pojedynczego objawu.

Na koniec dodaj test regresyjny, zaktualizuj dokumentację i przekaż zespołowi prostą regułę postępowania z danymi wejściowymi. Właśnie takie małe, powtarzalne procedury najskuteczniej zmniejszają ryzyko, że kolejna funkcja przywróci ten sam problem.

Artykuł ma charakter wyłącznie informacyjny i edukacyjny. Materiał został opracowany przy wsparciu nowoczesnych narzędzi analitycznych i językowych (AI). Przed podjęciem decyzji skonsultuj się z ekspertem.

FAQ - Najczęstsze pytania

Odmiana odbita wykorzystuje dane z bieżącego żądania i zwykle wymaga otwarcia przygotowanego linku. Składowana zapisuje niebezpieczną treść w bazie lub innym magazynie, przez co może zaatakować wielu użytkowników. DOM-based powstaje po stronie przeglądarki, gdy JavaScript przekazuje dane do niebezpiecznego miejsca w modelu DOM.

Filtr safe wyłącza automatyczne kodowanie wartości wstawianej do szablonu. Można go stosować tylko po wcześniejszej sanitizacji treści przez sprawdzone narzędzie, które dopuszcza wyłącznie określony podzbiór HTML. Samo filtrowanie słowa script nie zapewnia ochrony.

Tekst HTML, wartości atrybutów, adresy URL, CSS i JavaScript wymagają właściwych dla siebie metod kodowania. Atrybuty powinny być cytowane, adresy URL ograniczone do dozwolonych schematów, takich jak https, a danych użytkownika nie należy wklejać bezpośrednio do kodu JavaScript. Dla zwykłego tekstu w DOM bezpieczniejszy jest textContent niż innerHTML.

Nie. HttpOnly utrudnia odczyt ciasteczka przez JavaScript, Secure wymusza przesyłanie go przez HTTPS, a SameSite ogranicza wysyłanie ciasteczka między witrynami. Content Security Policy stanowi dodatkową warstwę ochrony, ale żadna z tych funkcji nie zastępuje poprawnego kodowania wyjścia.

Oceń artykuł

Ocena: 0.00 Liczba głosów: 0

Tagi

xss
jinja2
javascript
csp
sanitizacja
Autor Jeremi Andrzejewski
Jeremi Andrzejewski
Nazywam się Jeremi Andrzejewski i od 13 lat zajmuję się programowaniem, w szczególności w języku Python oraz nowoczesnymi technologiami. Moje zainteresowanie tymi tematami zaczęło się od pierwszych projektów, które realizowałem w szkole, a z czasem przerodziło się w pasję do rozwiązywania problemów i tworzenia innowacyjnych rozwiązań. Lubię dzielić się swoją wiedzą, szczególnie w zakresie analizy danych, automatyzacji procesów oraz tworzenia aplikacji webowych. W swojej pracy koncentruję się na dostarczaniu użytecznych, klarownych i aktualnych informacji. Staram się zawsze sprawdzać źródła, porównywać dostępne informacje i upraszczać skomplikowane zagadnienia, aby były zrozumiałe dla każdego. Wierzę, że odpowiednie zorganizowanie wiedzy oraz śledzenie najnowszych trendów w branży są kluczowe dla efektywnego nauczania i rozwoju. Cieszę się, że mogę dzielić się swoimi doświadczeniami na akademiapython.pl, gdzie mam nadzieję inspirować innych do odkrywania fascynującego świata programowania.

Udostępnij artykuł

Napisz komentarz