Ukratko: XSS u PHP-u sprječavate tako da svaki izlaz kodirate prema njegovu kontekstu: htmlspecialchars() za HTML, json_encode() sa zaštitnim opcijama za JavaScript, provjereni URL-ovi za poveznice. Predlošci s automatskim escapingom preuzimaju Vam velik dio toga. Content Security Policy i HttpOnly kolačići ograničavaju štetu ako ipak ostane ranjivost.
Zašto bi cross-site scripting sada trebalo provjeriti uz ostalo
Pozornost je u listopadu 2026. na PHP2Shellu, dvjema kritičnim ranjivostima u PHP interpreteru, za koje od 21.10.2026. postoje zakrpe. Mnoge tvrtke tim povodom daju provjeriti svoje web-aplikacije. To je dobar povod da se pogledaju i slabosti u vlastitom kodu koje nijedno PHP ažuriranje ne otklanja.
Cross-site scripting, skraćeno XSS, jedna je od njih. OWASP Top 10 vodi XSS unutar kategorije Injection. Za razliku od ranjivosti na poslužitelju, napad je ovdje usmjeren protiv posjetitelja i korisnika Vaše web-stranice, primjerice klijenata u shopu ili zaposlenika u backendu.
Posljedice mogu biti znatne: preuzete sesije administratora, manipulirani obrasci koji šalju podatke trećima ili krivotvoreni sadržaji na Vašoj web-stranici. Ako se preuzme administratorski račun, to može dovesti do potpune kontrole nad aplikacijom.
Kako nastaje XSS u PHP aplikacijama
XSS nastaje kad aplikacija podatke iz nepouzdanog izvora neprovjereno ispisuje na stranicu. Preglednik tada više ne može razlikovati Vaše sadržaje od ubačenog koda i izvršava oboje.
Razlikuju se tri oblika. Kod reflektiranog XSS-a sadržaj dolazi izravno iz aktualnog zahtjeva, primjerice iz pojma za pretragu u URL-u. Kod pohranjenog XSS-a nalazi se u bazi podataka, primjerice u komentaru ili polju profila, i pogađa svakoga tko otvori stranicu. DOM-based XSS nastaje u JavaScriptu preglednika kad skripte upisuju vrijednosti iz URL-a na stranicu.
Nepouzdani su svi podaci koji nisu čvrsto u kodu: polja obrazaca, URL parametri, kolačići, nazivi datoteka, odgovori vanjskih sučelja i sadržaji iz vlastite baze koji su u nekom trenutku došli izvana.
Ispravno kodiranje izlaza po kontekstu
Najvažnije pravilo glasi: kodirajte pri izlazu, i to prema mjestu na kojem vrijednost završava. Za HTML sadržaj i atribute u navodnicima htmlspecialchars() je sredstvo izbora. Mala pomoćna funkcija sprječava da se opcije zaborave:
<?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'] ?? '') ?>">
Drugi konteksti trebaju druga sredstva:
| Kontekst | Sigurna metoda |
|---|---|
| HTML sadržaj, atribut u navodnicima | htmlspecialchars() s ENT_QUOTES i UTF-8 |
| Podaci za JavaScript | json_encode() s JSON_HEX_TAG, JSON_HEX_AMP, JSON_HEX_APOS, JSON_HEX_QUOT |
| Parametar u URL-u | rawurlencode() za pojedinačnu vrijednost |
| Potpuni URL iz korisničkog unosa | Provjeriti shemu, dopustiti samo https i http, zatim htmlspecialchars() |
| Atributi bez navodnika, event handleri, CSS | izbjegavati, tamo ne umetati vrijednosti |
Za podatke koje skripta treba, atribut podataka ili JSON blok obično je najčišći put:
<?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>
U JavaScriptu vrijednost zatim čitate putem atributa podataka, a sadržaje postavljate s textContent umjesto s innerHTML.
Predlošci, čišćenje HTML-a i frameworki
Template engineovi poput Twiga i Bladea po zadanim postavkama automatski kodiraju izlaze. To isključuje velik dio tipičnih pogrešaka. Opasno postaje tamo gdje se to ponašanje namjerno isključi, primjerice filtrom raw u Twigu ili sintaksom {!! !!} u Bladeu. Takvih bi mjesta u kodu trebalo biti malo i treba ih ciljano provjeriti pri svakom pregledu.
Ako Vaša aplikacija namjerno treba dopuštati HTML korisnika, primjerice formatiranje u komentarima ili u sustavu za uredništvo, kodiranje nije dovoljno jer HTML treba biti prikazan. Tada trebate provjerenu biblioteku za čišćenje poput HTML Purifiera, koja propušta samo čvrst popis dopuštenih elemenata i atributa. Vlastiti filtri s regularnim izrazima prema iskustvu su manjkavi.
Posebno provjerite backend područja. XSS u prikazima koje vide samo administratori često se podcjenjuje, a pogađa upravo račune s najviše prava.
Pronalaženje XSS-a u postojećem PHP kodu
U razvijenim aplikacijama prvo pitanje obično nije kako ispravno kodirati, nego gdje se to dosad zaboravilo. Pragmatičan početak je potraga za mjestima na kojima se varijable ispisuju izravno:
# Izravni ispisi podataka zahtjeva
grep -rnE "(echo|print|<\?=)\s*\\\$_(GET|POST|REQUEST|COOKIE|SERVER)" src/ templates/ --include="*.php"
# Isključen auto-escaping u Twigu i Bladeu
grep -rnE "\|raw|\{!!" templates/ resources/views/
Pogoci su zadaci za provjeru, a ne gotovi nalazi. Pohranjeni XSS takva pretraga pronalazi samo uvjetno jer vrijednosti tamo dolaze iz baze podataka. Zato dodatno provjerite sve prikaze koji prikazuju sadržaje korisnika: komentare, profile, bilješke narudžbi, tikete podrške. Kod CMS-ova i shopova dodaci i teme trećih strana izričito spadaju u to jer se tamo nalazi velik dio logike izlaza.
Druga linija obrane: CSP i sigurni kolačići
Čak i uz pažljiv kod može se previdjeti jedno mjesto. Dvije mjere tada ograničavaju štetu. Content Security Policy pregledniku dopušta samo skripte iz određenih izvora i zabranjuje ugrađene skripte, osim ako nisu izričito odobrene:
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'"
Takvu politiku uvodite postupno, najprije u čistom načinu izvješćivanja putem Content-Security-Policy-Report-Only, jer postojeće ugradnje usluga analitike ili plaćanja obično treba dopuniti. Dodatna zaglavlja opisuje vodič Sigurnosna zaglavlja za web-stranice.
Uz to kolačići sesije trebaju imati atribut HttpOnly kako ih skripte ne bi mogle čitati. Kako osigurati sesije u cjelini, nalazi se u vodiču Osiguravanje sesija u PHP-u. Srodnu slabost na strani baze podataka obrađuje Sprječavanje SQL injectiona u PHP-u.
Kako PHP SOS konkretno pomaže?
- Provjeravamo predloške i izlaze Vaše aplikacije na nedostajuće ili pogrešno kodiranje.
- Nalaze ocjenjujemo prema dostupnosti i pogođenim računima te pokazujemo konkretne ispravke.
- Content Security Policy uvodimo postupno, bez ometanja funkcija shopa ili plaćanja.
- Nakon ispravka ponovno testiramo i dokumentiramo rezultat.
Neovisnu provjeru opisuje usluga Sigurnosni audit i penetracijski test.
Česta pitanja
Je li htmlspecialchars dovoljan protiv XSS-a?
Za običan tekst u HTML sadržaju i u atributima u navodnicima da, ako navedete ENT_QUOTES i skup znakova UTF-8. U JavaScriptu, u URL-ovima ili u CSS-u vrijede druga pravila, tamo treba vlastito, prikladno kodiranje.
Trebam li unose filtrirati pri spremanju ili pri izlazu?
Kodirajte pri izlazu, prema odredištu. Unose pri primitku provjerite na vjerodostojnost, primjerice duljinu i format. Tko filtrira samo pri spremanju, previđa podatke iz drugih izvora i otežava kasniju uporabu u drugim kontekstima.
Što donosi Content Security Policy?
Content Security Policy određuje iz kojih izvora preglednik smije učitavati i izvršavati skripte. Može znatno ograničiti posljedice previđene XSS ranjivosti, ali ne zamjenjuje ispravno kodiranje.
Kako dopustiti formatiranje poput podebljanog teksta ili poveznica u komentarima?
Koristite provjerenu biblioteku za čišćenje HTML-a s whitelistom dopuštenih elemenata i atributa. Vlastiti regularni izrazi za uklanjanje skripti podložni su pogreškama i ne bi ih trebalo koristiti.