Aktualna sytuacja: PHP2Shell. Dwie krytyczne luki w PHP (CVSS 9,2) umożliwiają wykonanie złośliwego kodu bez logowania. Poprawki od 21.10.2026.Co należy teraz zrobić →

Zapobieganie XSS w PHP: skuteczna obrona przed cross-site scripting

Jak programiści zapobiegają cross-site scripting w aplikacjach PHP: kodowanie zależne od kontekstu, auto-escaping w szablonach, oczyszczanie HTML i Content Security Policy.

Stan 5 min czytaniaPoradniki

Krótko: XSS w PHP zapobiegają Państwo, kodując każde wyjście odpowiednio do jego kontekstu: htmlspecialchars() dla HTML, json_encode() z opcjami ochronnymi dla JavaScript, sprawdzone adresy URL dla linków. Szablony z automatycznym escapingiem zdejmują z Państwa dużą część tej pracy. Content Security Policy i ciasteczka HttpOnly ograniczają szkodę, jeśli jednak pozostanie luka.

Dlaczego cross-site scripting warto sprawdzić teraz przy okazji

W październiku 2026 uwaga skupia się na PHP2Shell, dwóch krytycznych lukach w interpreterze PHP, dla których od 21.10.2026 dostępne są poprawki. Wiele firm każe przy tej okazji sprawdzać swoje aplikacje webowe. To dobry powód, by przyjrzeć się także podatnościom we własnym kodzie, których żadna aktualizacja PHP nie usuwa.

Cross-site scripting, w skrócie XSS, jest jedną z nich. OWASP Top 10 prowadzi XSS w ramach kategorii Injection. W przeciwieństwie do luki w serwerze atak jest tu skierowany przeciw odwiedzającym i użytkownikom Państwa strony, na przykład klientom sklepu lub pracownikom w panelu administracyjnym.

Skutki mogą być poważne: przejęte sesje administratorów, zmanipulowane formularze wysyłające dane osobom trzecim lub sfałszowane treści na Państwa stronie. Jeśli zostanie przejęte konto administratora, może to prowadzić aż do pełnej kontroli nad aplikacją.

Jak powstaje XSS w aplikacjach PHP?

XSS powstaje, gdy aplikacja wyświetla na stronie niesprawdzone dane z niegodnego zaufania źródła. Przeglądarka nie potrafi wtedy odróżnić Państwa treści od wprowadzonego kodu i wykonuje jedno i drugie.

Rozróżnia się trzy postaci. W przypadku odbitego XSS treść pochodzi bezpośrednio z bieżącego żądania, na przykład z wyszukiwanego hasła w adresie URL. W przypadku przechowywanego XSS leży w bazie danych, na przykład w komentarzu lub polu profilu, i dotyka każdego, kto otworzy stronę. XSS oparty na DOM powstaje w JavaScript przeglądarki, gdy skrypty zapisują na stronie wartości z adresu URL.

Niegodne zaufania są wszystkie dane, które nie są na stałe w kodzie: pola formularzy, parametry URL, ciasteczka, nazwy plików, odpowiedzi zewnętrznych interfejsów i treści z własnej bazy danych, które kiedyś przyszły z zewnątrz.

Prawidłowe kodowanie danych wyjściowych zależnie od kontekstu

Najważniejsza zasada brzmi: kodować przy wyświetlaniu, i to odpowiednio do miejsca, w którym wartość trafia. Dla treści HTML i atrybutów w cudzysłowach środkiem z wyboru jest htmlspecialchars(). Mała funkcja pomocnicza zapobiega pominięciu opcji:

<?php
function e(?string $wert): string
{
    return htmlspecialchars($wert ?? '', ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5, 'UTF-8');
}
?>
<h2><?= e($produkt['name']) ?></h2>
<input type="text" name="suche" value="<?= e($_GET['q'] ?? '') ?>">

Inne konteksty wymagają innych środków:

Kontekst Bezpieczna metoda
Treść HTML, atrybut w cudzysłowach htmlspecialchars() z ENT_QUOTES i UTF-8
Dane dla JavaScript json_encode() z JSON_HEX_TAG, JSON_HEX_AMP, JSON_HEX_APOS, JSON_HEX_QUOT
Parametr w adresie URL rawurlencode() dla pojedynczej wartości
Pełny adres URL z danych użytkownika sprawdzić schemat, dopuścić tylko https i http, potem htmlspecialchars()
Atrybuty bez cudzysłowów, obsługa zdarzeń, CSS unikać, nie wstawiać tam wartości

Dla danych potrzebnych skryptowi najczystszą drogą jest zwykle atrybut danych lub blok JSON:

<?php
$daten = json_encode($konfiguration, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT);
?>
<div id="app" data-konfig="<?= e($daten) ?>"></div>

W JavaScript odczytują Państwo wartość przez atrybut danych i wstawiają treści za pomocą textContent zamiast innerHTML.

Szablony, oczyszczanie HTML i frameworki

Silniki szablonów takie jak Twig i Blade domyślnie automatycznie kodują dane wyjściowe. To wyklucza dużą część typowych błędów. Niebezpiecznie robi się tam, gdzie to zachowanie jest świadomie wyłączane, na przykład filtrem raw w Twig lub składnią {!! !!} w Blade. Takie miejsca powinny być w kodzie rzadkie i celowo sprawdzane przy każdym przeglądzie.

Jeśli Państwa aplikacja ma świadomie dopuszczać HTML od użytkowników, na przykład formatowanie w komentarzach lub w systemie redakcyjnym, samo kodowanie nie wystarcza, ponieważ HTML ma być przecież wyświetlany. Potrzebna jest wtedy sprawdzona biblioteka do oczyszczania, taka jak HTML Purifier, która przepuszcza tylko stałą listę dozwolonych elementów i atrybutów. Własne filtry z wyrażeniami regularnymi są według doświadczenia dziurawe.

Ważne

Proszę szczególnie sprawdzić obszary backendu. XSS w widokach, które widzą tylko administratorzy, bywa niedoceniany, a trafia właśnie w konta z największymi uprawnieniami.

Wyszukiwanie XSS w istniejącym kodzie PHP

W aplikacjach rozwijanych latami pierwszym pytaniem zwykle nie jest, jak prawidłowo kodować, ale gdzie dotąd o tym zapomniano. Pragmatycznym początkiem jest wyszukanie miejsc, w których zmienne są wyświetlane bezpośrednio:

# Bezpośrednie wyświetlanie danych żądania
grep -rnE "(echo|print|<\?=)\s*\\\$_(GET|POST|REQUEST|COOKIE|SERVER)" src/ templates/ --include="*.php"
# Wyłączony auto-escaping w Twig i Blade
grep -rnE "\|raw|\{!!" templates/ resources/views/

Trafienia są zadaniami kontrolnymi, a nie gotowymi ustaleniami. Przechowywany XSS takie wyszukiwanie znajduje tylko w ograniczonym stopniu, ponieważ wartości pochodzą tam z bazy danych. Proszę dlatego dodatkowo sprawdzić wszystkie widoki wyświetlające treści użytkowników: komentarze, profile, notatki do zamówień, zgłoszenia do wsparcia. W CMS i sklepach wyraźnie należą do tego wtyczki i motywy zewnętrznych dostawców, ponieważ tam znajduje się duża część logiki wyświetlania.

Druga linia obrony: CSP i bezpieczne ciasteczka

Także przy starannym kodzie jakieś miejsce może zostać przeoczone. Dwa środki ograniczają wtedy szkodę. Content Security Policy pozwala przeglądarce na skrypty tylko z określonych źródeł i zabrania skryptów osadzonych, o ile nie są wyraźnie dopuszczone:

Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'"

Taką politykę proszę wprowadzać stopniowo, najpierw w trybie samego raportowania przez Content-Security-Policy-Report-Only, ponieważ istniejące integracje usług analitycznych lub płatniczych zwykle trzeba uzupełnić. Dalsze nagłówki opisuje poradnik Nagłówki bezpieczeństwa dla stron WWW.

Dodatkowo ciasteczka sesji powinny mieć atrybut HttpOnly, aby skrypty nie mogły ich odczytać. Jak zabezpieczyć sesje w całości, opisuje poradnik Zabezpieczanie sesji w PHP. Pokrewną podatnością po stronie bazy danych zajmuje się Jak zapobiegać SQL injection w PHP.

Jak konkretnie pomaga PHP SOS?

  • Sprawdzamy szablony i dane wyjściowe Państwa aplikacji pod kątem brakującego lub błędnego kodowania.
  • Oceniamy znaleziska według osiągalności i dotkniętych kont oraz pokazujemy konkretne poprawki.
  • Wprowadzamy Content Security Policy stopniowo, nie zakłócając funkcji sklepu ani płatności.
  • Po poprawce testujemy ponownie i dokumentujemy wynik.

Niezależną kontrolę opisuje usługa Audyt bezpieczeństwa i test penetracyjny.

Częste pytania

Czy htmlspecialchars wystarcza przeciw XSS?

Dla zwykłego tekstu w treści HTML i w atrybutach w cudzysłowach tak, jeśli podadzą Państwo ENT_QUOTES i zestaw znaków UTF-8. W JavaScript, w adresach URL czy w CSS obowiązują inne zasady, tam potrzebne jest własne, odpowiednie kodowanie.

Czy dane wejściowe filtrować przy zapisie, czy przy wyświetlaniu?

Kodować przy wyświetlaniu, odpowiednio do celu. Dane wejściowe warto przy odbiorze sprawdzać pod kątem wiarygodności, na przykład długości i formatu. Kto filtruje tylko przy zapisie, pomija dane z innych źródeł i utrudnia późniejsze użycie w innych kontekstach.

Co daje Content Security Policy?

Content Security Policy określa, z jakich źródeł przeglądarka może ładować i wykonywać skrypty. Może wyraźnie ograniczyć skutki przeoczonej luki XSS, ale nie zastępuje prawidłowego kodowania.

Jak zezwolić na formatowanie, takie jak pogrubienie czy linki, w komentarzach?

Proszę użyć sprawdzonej biblioteki do oczyszczania HTML z białą listą dozwolonych elementów i atrybutów. Własne wyrażenia regularne do usuwania skryptów są podatne na błędy i nie powinny być stosowane.

Źródła

Kontakt natychmiast

Liczy się każda godzina. Proszę napisać do nas teraz.

Proszę krótko opisać system, wersję PHP i obserwacje. Skontaktujemy się z Państwem, ustalimy sytuację i będziemy pracować zdalnie, aż incydent zostanie zamknięty i udokumentowany.

Natychmiastowa pomoc