Header di sicurezza WordPress: CSP, HSTS e altri
Header di sicurezza WordPress spiegati: valori iniziali sicuri per HSTS, CSP, Referrer-Policy e Permissions-Policy, dove impostarli e come evitare malfunzionamenti.
Anche in: English Deutsch Français Español Polski Português Türkçe
La maggior parte dei siti WordPress può risolvere una valutazione insufficiente degli header in meno di un'ora. Basta aggiungere oggi quattro header a basso rischio: HSTS, X-Content-Type-Options, Referrer-Policy e Permissions-Policy. Content-Security-Policy va introdotta con gradualità, partendo dalla modalità report-only. Questa guida indica i valori iniziali sicuri per ciascuno di questi header di sicurezza di WordPress, dove configurarli e quali possono compromettere l'editor o gli incorporamenti (embed).
Cosa fanno gli header di sicurezza di WordPress e cosa no
Gli header di sicurezza sono istruzioni che il server invia al browser del visitatore. Dicono al browser di usare solo HTTPS, di non indovinare il tipo dei file, di non lasciare che altri siti incorporino le vostre pagine in un frame e di stabilire quali script può eseguire. In questo modo riducono gli attacchi di downgrade, il MIME sniffing, il clickjacking e l'impatto del cross-site scripting (XSS).
Non correggono un plugin vulnerabile, non fermano un attacco a forza bruta sulle password e non proteggono il server in sé. Se un aggressore riesce a scrivere file PHP sul server, nessun header serve a qualcosa. Gli header sono un livello di hardening lato browser, non una correzione di sicurezza.
L'assenza di header è un tipico esempio di configurazione errata della sicurezza (security misconfiguration) nella OWASP Top 10. Si tratta inoltre di misure economiche e ben documentate, che rientrano nello «stato dell'arte» da considerare ai sensi dell'articolo 32 del GDPR.
Il livello di base: valori iniziali sicuri per ogni header
Questi valori sono volutamente prudenti. Non dovrebbero compromettere un normale sito WordPress e potranno essere resi più restrittivi in seguito.
| Header | Funzione | Valore iniziale sicuro |
|---|---|---|
| Strict-Transport-Security | Impone HTTPS nelle visite successive | max-age=300 |
| X-Content-Type-Options | Impedisce di indovinare il tipo MIME | nosniff |
| Referrer-Policy | Limita i dati dell'URL inviati ad altri siti | strict-origin-when-cross-origin |
| Permissions-Policy | Disattiva le funzioni del browser non usate | camera=(), microphone=(), geolocation=() |
| Content-Security-Policy | Protezione dai frame (per ora) | frame-ancestors 'self' |
| X-Frame-Options | Protezione dai frame (legacy) | SAMEORIGIN |
HSTS è definito nella RFC 6797. Si parte da cinque minuti (max-age=300) e si verifica che nulla si rompa. Poi si passa a un giorno (86400), a una settimana e infine a un anno (31536000). includeSubDomains va aggiunto solo dopo aver controllato che ogni sottodominio serva HTTPS.
Referrer-Policy strict-origin-when-cross-origin invia agli altri siti soltanto il vostro dominio, non l'URL completo. Su WordPress conta, perché gli URL contengono spesso query string con termini di ricerca, ID d'ordine o parametri di campagna che non devono arrivare a terzi.
Permissions-Policy disattiva fotocamera, microfono e geolocalizzazione per le vostre pagine e per i frame incorporati. Se avete un localizzatore di negozi che richiede la posizione, escludete geolocation dall'elenco.
La protezione dai frame ora spetta alla CSP, con frame-ancestors 'self'. X-Frame-Options: SAMEORIGIN va mantenuto solo come ripiego per i browser datati. Una piccola CSP applicata che contenga solo frame-ancestors si può attivare subito senza rischi. La CSP completa verrà dopo.
X-XSS-Protection è obsoleto. Non attivatelo. Controllava un filtro XSS che i browser moderni hanno rimosso, e il filtro stesso poteva essere sfruttato per creare nuovi problemi su pagine altrimenti sicure. Impostare 1; mode=block non porta alcun vantaggio. La sostituta è la CSP.
Dove impostare gli header di sicurezza: server, plugin o CDN
Scegliete un solo punto per ogni header. Se server, plugin di sicurezza e CDN inviano tutti Strict-Transport-Security, si ottengono valori duplicati o in conflitto e nessuno sa più quale sia quello attivo.
Di solito il posto migliore è il web server. Le regole del server valgono per ogni risposta, comprese immagini, file CSS e pagine servite da una cache di pagina completa. Su Apache, presente nella maggior parte degli hosting condivisi, aggiungete queste righe al file .htaccess nella root di WordPress, sopra il blocco # BEGIN WordPress. Serve mod_headers:
<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=300"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
Header always set Content-Security-Policy "frame-ancestors 'self'"
Header always set X-Frame-Options "SAMEORIGIN"
</IfModule>
Su nginx inserite le righe equivalenti nel blocco server del sito HTTPS ed eseguite nginx -t prima di ricaricare la configurazione:
add_header Strict-Transport-Security "max-age=300" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Content-Security-Policy "frame-ancestors 'self'" always;
add_header X-Frame-Options "SAMEORIGIN" always;
Attenzione a un'insidia di nginx. Un blocco location con una propria riga add_header non eredita nessuna riga add_header del livello server. Un header di cache-control aggiunto per /wp-content/uploads/ può eliminare in silenzio tutti gli header di sicurezza su quei file. Ripetete gli header in quel blocco, oppure spostateli in uno snippet da richiamare ovunque con include.
I plugin e il codice che usano l'hook send_headers di WordPress entrano in azione solo quando PHP genera la pagina. I file statici non passano mai da PHP. Anche le pagine servite da una cache di pagina completa possono saltare PHP, a seconda di come la cache memorizza e riproduce gli header. Le regole della CDN o dell'edge sono una buona alternativa, perché coprono tutto ciò che passa dalla CDN. Gli hosting gestiti offrono spesso impostazioni degli header nel pannello di controllo. Se è il vostro caso, usate quel pannello e non duplicate le regole in .htaccess.
Content-Security-Policy senza rompere WordPress
La CSP è l'header con il maggior beneficio e il rischio più alto. Indica al browser da quali fonti possono provenire script, stili, immagini, font e frame. WordPress rende il compito difficile. Il core e l'editor a blocchi generano script e stili inline. I page builder ne aggiungono altri. Strumenti di analytics, incorporamenti di YouTube e mappe, banner dei cookie e widget di pagamento caricano ciascuno codice da altri domini. Una policy rigida che blocchi uno di questi elementi può compromettere il checkout o lasciare l'editor con una schermata vuota.
Si parte dalla modalità report-only. Il browser registra le violazioni senza bloccare nulla:
Content-Security-Policy-Report-Only: default-src 'self'; img-src 'self' data:; object-src 'none'; base-uri 'self'
Aprite i principali tipi di pagina: home, un articolo del blog, una pagina con un incorporamento, il carrello e il checkout. Poi aprite gli strumenti per sviluppatori del browser e controllate la Console per ognuna. Ogni violazione indica la risorsa bloccata e la direttiva infranta. Considerate l'elenco come un inventario delle richieste a terzi che il sito effettua. Spesso emergono elementi che nessuno ricorda di aver aggiunto, come un vecchio widget di chat o font caricati da Google. Il nostro articolo su Google Fonts spiega come gestirli.
Aggiungete le fonti che servono davvero, togliete quelle superflue e controllate di nuovo la Console. Passate al nome Content-Security-Policy solo quando le violazioni sono cessate su tutte le pagine importanti. Nella policy completa includete frame-ancestors 'self': i browser ignorano frame-ancestors in modalità report-only e l'header completo sostituirà quello piccolo impostato in precedenza. Gli script inline sono la parte difficile: consentire 'unsafe-inline' elimina buona parte della protezione dall'XSS, mentre nonce o hash richiedono il supporto del tema o del plugin. Il riferimento MDN sulla CSP documenta ogni direttiva.
Valutate di tenere wp-admin fuori dalla policy restrittiva, almeno all'inizio. Su nginx impostate la CSP del front-end nelle location che servono il sito pubblico, non nell'intero blocco server.
Si può aggiungere un endpoint report-uri o report-to per raccogliere le violazioni in modo centralizzato. Questi report contengono gli URL delle pagine e dettagli sul browser, e ciascuno arriva con l'indirizzo IP del visitatore. Un servizio di reporting di terzi agisce in genere come vostro responsabile del trattamento ai sensi del GDPR, quindi serve un accordo sul trattamento dei dati (art. 28). Per un sito piccolo, spesso basta leggere la Console in autonomia.
HSTS preload: perché aspettare
Il preload inserisce il vostro dominio in un elenco distribuito insieme ai browser. Questi browser usano poi HTTPS per il vostro dominio già dalla prima visita, e per ogni sottodominio. Ed è proprio questo il problema. Un vecchio host di webmail, un sito di staging, l'interfaccia di una stampante o uno strumento intranet che funziona solo su HTTP semplice diventano irraggiungibili con quei browser.
Rimuovere un dominio dall'elenco è possibile, ma lento. I browser eliminano la voce solo quando la modifica arriva agli utenti con gli aggiornamenti del browser. Conviene ragionare come se la decisione fosse permanente.
Aumentate max-age per gradi e mantenete a lungo i valori più alti. Elencate ogni sottodominio usato dalla vostra organizzazione, compresi quelli presenti nel DNS che nessuno visita più. Prendete in considerazione il preload solo quando tutti servono HTTPS. Il sito di sottomissione hstspreload.org pubblica i requisiti aggiornati per l'header. Leggeteli lì, invece di copiare valori da articoli di blog.
Verificare gli header e mantenerli attivi
Controllate le risposte effettive, non i file di configurazione. Negli strumenti per sviluppatori del browser aprite la scheda Network, fate clic sulla richiesta del documento e leggete gli header della risposta. Da terminale:
curl -sI https://example.com/
curl -sI https://example.com/wp-includes/css/dist/block-library/style.min.css
I siti pubblici di verifica degli header offrono una rapida panoramica. L'OWASP Secure Headers Project spiega le ragioni di ciascun header raccomandato. Provate almeno queste cinque risposte:
- Home page
- Una pagina servita dalla cache, aperta senza essere autenticati
- Un file statico (CSS, JS o immagine)
/wp-login.php- Una schermata di wp-admin, compreso l'editor a blocchi
Gli header tendono a sparire senza che nessuno se ne accorga. Le cause più comuni sono una migrazione di hosting che perde le regole di .htaccess, un cambio di CDN, o un nuovo plugin di cache o di sicurezza che invia valori propri. Dopo ognuna di queste modifiche ricontrollate gli header. Conservate la configurazione attiva e la data del test insieme agli altri documenti di hardening, nella checklist di sicurezza. Quella documentazione è ciò che mostrerete a un cliente che invia un questionario o a un auditor.
Il plugin di audit di secaudit.eu esegue una scansione di hardening e configurazione e riconduce i risultati all'art. 32 del GDPR e all'art. 21 della direttiva NIS2. Usatelo insieme ai controlli degli header descritti sopra → secaudit.eu
FAQ
Gli header di sicurezza influiscono su SEO o velocità di caricamento?
L'effetto è trascurabile. Aggiungono poche centinaia di byte a ogni risposta. HSTS può far risparmiare il reindirizzamento da HTTP a HTTPS nelle visite successive.
Un plugin può impostare al posto mio tutti gli header di sicurezza?
In parte. Un plugin copre le pagine generate da PHP, ma può non raggiungere i file statici e le pagine in cache. Le regole del server o della CDN sono più affidabili.
Conviene inviare X-XSS-Protection: 0 o ometterlo?
Ometterlo, oppure inviare 0 se un questionario pretende un valore esplicito. 0 disattiva il filtro legacy. Non usate mai 1; mode=block.
L'HSTS è richiesto dal GDPR?
Nessuna norma del GDPR cita header specifici. Il GDPR richiede però misure di sicurezza adeguate, che tengano conto dello stato dell'arte. Imporre HTTPS con HSTS è una misura standard e poco costosa, coerente con tale requisito.
Questo articolo ha carattere informativo generale e non costituisce consulenza legale.
Controlla il tuo sito
Security Audit & Compliance è il plugin WordPress di secaudit.eu: cerca malware, spam nascosto, file core modificati e codice vulnerabile, e collega i risultati all’art. 32 del GDPR, all’art. 21 della NIS2 e alla OWASP Top 10.
Scopri cosa controlla il plugin