In breve: XSS in PHP si previene codificando ogni output in modo adatto al suo contesto: htmlspecialchars() per HTML, json_encode() con opzioni di protezione per JavaScript, URL verificati per i link. I template con escaping automatico ne tolgono gran parte dal Suo lavoro. Una Content Security Policy e i cookie HttpOnly limitano il danno se comunque resta una vulnerabilità.
Perché ora il cross-site scripting va controllato insieme al resto
A ottobre 2026 l'attenzione è su PHP2Shell, due vulnerabilità critiche nell'interprete PHP, per le quali esistono patch dal 21/10/2026. Molte aziende, in questo contesto, fanno controllare le proprie applicazioni web. È una buona occasione per esaminare anche le debolezze nel codice proprio che nessun aggiornamento di PHP risolve.
Il cross-site scripting, in breve XSS, è una di queste. La OWASP Top 10 colloca XSS nella categoria Injection. A differenza di una vulnerabilità nel server, qui l'attacco è rivolto ai visitatori e agli utenti del Suo sito, ad esempio ai clienti nello shop o ai collaboratori nel backend.
Le conseguenze possono essere considerevoli: sessioni di amministratori prese in carico, moduli manipolati che inviano dati a terzi, o contenuti falsificati sul Suo sito. Se un account amministratore viene preso in carico, si può arrivare al controllo completo dell'applicazione.
Come nasce XSS nelle applicazioni PHP?
XSS nasce quando un'applicazione restituisce in una pagina, senza verifica, dati provenienti da una fonte non affidabile. Il browser non riesce più a distinguere tra i Suoi contenuti e il codice introdotto di nascosto e li esegue entrambi.
Si distinguono tre forme. Nel XSS riflesso il contenuto proviene direttamente dalla richiesta corrente, ad esempio da un termine di ricerca nell'URL. Nel XSS memorizzato si trova nel database, per esempio in un commento o in un campo del profilo, e colpisce chiunque apra la pagina. Il XSS basato su DOM nasce nel JavaScript del browser, quando gli script scrivono nella pagina valori presi dall'URL.
Non sono affidabili tutti i dati che non sono fissati nel codice: campi dei moduli, parametri URL, cookie, nomi di file, risposte di interfacce esterne e contenuti del proprio database che a un certo punto sono arrivati dall'esterno.
Codificare correttamente l'output in base al contesto
La regola più importante è: codificare all'output, e in modo adatto al luogo in cui finisce il valore. Per il contenuto HTML e gli attributi tra virgolette, htmlspecialchars() è lo strumento da scegliere. Una piccola funzione di supporto evita che le opzioni vengano dimenticate:
<?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'] ?? '') ?>">
Altri contesti richiedono altri strumenti:
| Contesto | Metodo sicuro |
|---|---|
| Contenuto HTML, attributo tra virgolette | htmlspecialchars() con ENT_QUOTES e UTF-8 |
| Dati per JavaScript | json_encode() con JSON_HEX_TAG, JSON_HEX_AMP, JSON_HEX_APOS, JSON_HEX_QUOT |
| Parametro in un URL | rawurlencode() per il singolo valore |
| URL completo da input dell'utente | verificare lo schema, consentire solo https e http, poi htmlspecialchars() |
| Attributi senza virgolette, event handler, CSS | evitare, non inserire lì dei valori |
Per i dati di cui uno script ha bisogno, un attributo data o un blocco JSON è di solito la via più pulita:
<?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>
In JavaScript legge poi il valore tramite l'attributo data e imposta i contenuti con textContent invece che con innerHTML.
Template, pulizia dell'HTML e framework
I motori di template come Twig e Blade codificano per impostazione predefinita gli output in modo automatico. Questo esclude gran parte degli errori tipici. Il pericolo sorge dove questo comportamento viene disattivato di proposito, ad esempio con il filtro raw in Twig o la sintassi {!! !!} in Blade. Tali punti dovrebbero essere rari nel codice e controllati in modo mirato a ogni review.
Se la Sua applicazione deve consentire di proposito HTML da parte degli utenti, ad esempio formattazioni nei commenti o in un sistema editoriale, codificare non basta, perché l'HTML deve essere visualizzato. Serve allora una libreria di pulizia collaudata come HTML Purifier, che lascia passare solo un elenco fisso di elementi e attributi consentiti. I filtri propri con espressioni regolari sono, per esperienza, lacunosi.
Controlli in particolare le aree di backend. XSS nelle viste che vedono solo gli amministratori è spesso sottovalutato, ma colpisce proprio gli account con più diritti.
Trovare XSS nel codice PHP esistente
Nelle applicazioni cresciute negli anni la prima domanda di solito non è come codificare correttamente, ma dove finora ci si è dimenticati di farlo. Un inizio pragmatico è la ricerca dei punti in cui le variabili vengono emesse direttamente:
# Output diretti di dati della richiesta
grep -rnE "(echo|print|<\?=)\s*\\\$_(GET|POST|REQUEST|COOKIE|SERVER)" src/ templates/ --include="*.php"
# Auto-escaping disattivato in Twig e Blade
grep -rnE "\|raw|\{!!" templates/ resources/views/
I riscontri sono incarichi di verifica, non risultati definitivi. Il XSS memorizzato una ricerca di questo tipo lo trova solo in parte, perché lì i valori vengono dal database. Controlli quindi in aggiunta tutte le viste che mostrano contenuti degli utenti: commenti, profili, note d'ordine, ticket di supporto. Nei CMS e negli shop vanno inclusi espressamente plugin e temi di terze parti, perché lì si trova gran parte della logica di output.
Seconda linea di difesa: CSP e cookie sicuri
Anche con un codice accurato un punto può essere trascurato. Due misure limitano allora il danno. Una Content Security Policy consente al browser solo script provenienti da fonti stabilite e vieta gli script incorporati, a meno che non siano autorizzati espressamente:
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'"
Introduca una simile policy gradualmente, dapprima in sola modalità di segnalazione tramite Content-Security-Policy-Report-Only, perché le integrazioni esistenti di servizi di analisi o di pagamento vanno di solito aggiunte. Altri header sono descritti nella guida Header di sicurezza per i siti web.
Inoltre i cookie di sessione dovrebbero avere l'attributo HttpOnly, in modo che gli script non possano leggerli. Come mettere in sicurezza le sessioni nel complesso è spiegato nella guida Mettere in sicurezza le sessioni in PHP. Una debolezza affine sul lato database è trattata in Prevenire la SQL injection in PHP.
Come aiuta concretamente PHP SOS?
- Verifichiamo template e output della Sua applicazione alla ricerca di codifiche mancanti o errate.
- Valutiamo i ritrovamenti in base a raggiungibilità e account coinvolti e mostriamo correzioni concrete.
- Introduciamo una Content Security Policy gradualmente, senza disturbare le funzioni dello shop o dei pagamenti.
- Dopo la correzione testiamo di nuovo e documentiamo il risultato.
Una verifica indipendente è descritta nel servizio Audit di sicurezza e penetration test.
Domande frequenti
htmlspecialchars basta contro XSS?
Per il testo normale nel contenuto HTML e negli attributi tra virgolette sì, se indica ENT_QUOTES e il set di caratteri UTF-8. In JavaScript, negli URL o in CSS valgono altre regole e serve una codifica propria e adatta.
Dovrei filtrare gli input al salvataggio o all'output?
Codifichi all'output, in modo adatto alla destinazione. Gli input vanno controllati alla ricezione per plausibilità, ad esempio lunghezza e formato. Chi filtra solo al salvataggio trascura i dati provenienti da altre fonti e complica l'uso successivo in altri contesti.
Che cosa serve una Content Security Policy?
Una Content Security Policy stabilisce da quali fonti il browser può caricare ed eseguire script. Può limitare in modo significativo gli effetti di una vulnerabilità XSS trascurata, ma non sostituisce una codifica corretta.
Come consento formattazioni come grassetto o link nei commenti?
Usi una libreria collaudata di pulizia dell'HTML con una whitelist di elementi e attributi consentiti. Le espressioni regolari proprie per rimuovere gli script sono soggette a errori e non andrebbero usate.