Web Application Firewall (WAF) su Nginx: ModSecurity per applicazioni PHP senza falsi positivi
Il 9 ottobre 2024 mi ha contattato il responsabile IT di una piattaforma di e-learning B2B piemontese - 85 aziende clienti paganti fra scuole private e aziende di formazione professionale, circa 42.000 utenti finali attivi sulla piattaforma Laravel 10, fatturato annuo di circa 2,8 milioni di euro in modello SaaS. Il responsabile IT aveva subito la notifica di un tentativo di SQL injection parziale rilevato nei log MySQL di produzione - una query anomala che non aveva prodotto danni reali (grazie ai prepared statement applicativi) ma che segnalava che qualcuno stava attivamente scansionando l'applicazione cercando vulnerabilità. Il tentativo non era particolarmente sofisticato ma aveva fatto alzare l'attenzione del management, che ora chiedeva un livello aggiuntivo di difesa strutturale.
La raccomandazione che ho dato al cliente è stata introdurre un Web Application Firewall (WAF) davanti all'applicazione, per catturare automaticamente la stragrande maggioranza degli attacchi automatizzati prima che raggiungano il codice applicativo. La scelta specifica è stata ModSecurity con OWASP Core Rule Set, su Nginx già presente in infrastruttura. In cinque giornate distribuite in tre settimane, ho installato e configurato ModSecurity, attivato le regole CRS di default, eseguito tuning completo per eliminare i falsi positivi sull'applicazione specifica del cliente, messo in produzione in modalità blocking dopo due settimane di osservazione in audit mode. Al termine dell'intervento, il WAF ha catturato e bloccato automaticamente circa 1.200 richieste malevole nei primi 30 giorni di produzione attiva - da script di scansione automatica (sqlmap, nikto, nuclei), tentativi di exploit di CMS comuni (WordPress non presente ma comunque tentato), brute force di endpoint di autenticazione. Zero falsi positivi su traffico legittimo post-tuning. Costo consulenziale: 3.200 euro. Costo operativo ricorrente: zero (ModSecurity è open source, gira sullo stesso Nginx esistente).
Questo articolo descrive il pattern di implementazione di ModSecurity come WAF produttivo per applicazioni PHP su Nginx, basato sull'esperienza di circa 12 progetti simili negli ultimi quattro anni. Il principio guida è uno: un WAF mal configurato produce più problemi di quanti ne risolva - blocca traffico legittimo, genera fatigue di alert, erode fiducia nel sistema. Il tuning è il lavoro vero, non l'installazione.
Perché un WAF è una linea di difesa strategica per applicazioni PHP moderne
Un Web Application Firewall analizza le richieste HTTP in ingresso prima che raggiungano l'applicazione, applicando regole che identificano e bloccano pattern di attacco noti - SQL injection, cross-site scripting, command injection, local file inclusion, protocol attacks, bot automatizzati malevoli. La documentazione ufficiale OWASP sul WAF e sulla ModSecurity Core Rule Set è il riferimento canonico per comprendere il modello.
Il valore architetturale di un WAF non è sostituire le protezioni applicative (prepared statement, output encoding, validazione input) - quelle restano essenziali - ma aggiungere un layer difensivo esterno che cattura attacchi prima che richiedano al codice applicativo di difendersi. Questo layer ha quattro benefici specifici.
Primo beneficio: difesa in profondità. Se una vulnerabilità applicativa viene introdotta accidentalmente in un deploy, il WAF può comunque bloccare gli attacchi che la sfruttano, dando tempo al team di fixare. Secondo beneficio: riduzione del carico applicativo. Le richieste malevole vengono filtrate prima di raggiungere PHP, riducendo il carico CPU/memoria sui server applicativi. Terzo beneficio: visibilità operativa. I log del WAF forniscono telemetria dettagliata sugli attacchi tentati, utile per awareness e per adattamento delle difese. Quarto beneficio: compliance. Molti framework regolamentari (PCI-DSS per pagamenti, NIS2 per servizi digitali critici) richiedono esplicitamente presenza di controlli applicativi equivalenti a un WAF.
Se gestisci un'applicazione PHP in produzione esposta a internet e non hai ancora un WAF, nel mio profilo professionale trovi il dettaglio degli interventi di hardening web che ho condotto in contesti PMI italiane, con configurazioni calibrate sull'applicazione reale del cliente.
ModSecurity con OWASP CRS: lo stack open source standard
ModSecurity è il WAF open source più maturo e ampiamente adottato, originariamente sviluppato come modulo Apache, poi portato come plugin per Nginx, e ora maintained dalla community come progetto indipendente (dopo che TrustWave ne ha depositato la manutenzione nel 2024). OWASP Core Rule Set (CRS) è il set di regole di default distribuito con ModSecurity, che implementa protezioni per le vulnerabilità OWASP Top 10 e una vasta gamma di attacchi conosciuti.
Il setup produzione che applico su Nginx include quattro componenti. Primo componente: libmodsecurity3 - la libreria C++ che implementa il motore ModSecurity. Seconda componente: ModSecurity-nginx connector - il modulo Nginx che integra libmodsecurity nel request processing. Terzo componente: OWASP CRS - il set di regole pre-scritte. Quarto componente: configurazione di tuning custom per l'applicazione specifica.
L'installazione su Debian 12 richiede tipicamente compilazione di Nginx con il modulo ModSecurity - non è disponibile nei pacchetti apt di default. Il processo richiede circa 30-60 minuti di lavoro iniziale ma è ben documentato e automatizzabile via script per deploy futuri.
Il processo di tuning: audit mode, analisi, whitelist chirurgiche
Il passaggio più critico di un deployment ModSecurity è il tuning - adattamento delle regole all'applicazione specifica per eliminare falsi positivi prima di attivare il blocco reale. Saltare questa fase e attivare direttamente il blocking produce disastri operativi: legitimate user vengono bloccati da regole troppo aggressive, il team viene sommerso di alert, il WAF viene disabilitato d'emergenza dal management entro 24-48 ore.
Il processo di tuning che applico ha quattro fasi sequenziali.
Fase 1 - Installazione in audit mode (settimana 1). ModSecurity viene attivato con SecRuleEngine DetectionOnly - rileva e logga le richieste che sarebbero bloccate, ma non le blocca effettivamente. L'applicazione continua a funzionare normalmente, ma il log ModSecurity raccoglie visibilità su cosa il WAF bloccherebbe. Questa fase è critica e non va saltata - fornisce i dati oggettivi per il tuning.
Fase 2 - Analisi dei log (settimana 2). Dopo 7-14 giorni di audit mode, analizzo i log ModSecurity per identificare i pattern di falsi positivi. Le regole che scattano più frequentemente su traffico legittimo sono candidate per whitelist chirurgica. Il pattern operativo è:
# top 20 regole che scattano più spesso
cat /var/log/modsec_audit.log | grep 'id "[0-9]*"' | sort | uniq -c | sort -rn | head -20Per il cliente piemontese, l'analisi ha rivelato tre categorie di falsi positivi. Prima categoria: regole che segnalavano POST con body JSON complessi (richieste API interne dell'applicazione) come potenziali attacchi. Seconda categoria: regole SQL injection che triggeravano su specifici nomi di campi del database dell'applicazione (parole come 'select' o 'union' nei titoli dei corsi). Terza categoria: regole XSS che bloccavano l'upload di contenuti HTML formattati dai docenti per le lezioni.
Fase 3 - Whitelist chirurgiche (settimana 3). Per ogni falso positivo identificato, creazione di una regola di whitelist precisa che disabilita la regola CRS specifica solo per il path o il parametro specifico dell'applicazione interessato. Il pattern corretto è whitelist granulare, non disabilitazione globale della regola. Esempio:
SecRule REQUEST_URI "@beginsWith /api/courses" \
"id:1000,phase:1,pass,nolog,\
ctl:ruleRemoveById=942100;ctl:ruleRemoveById=942110"Questo disabilita le regole 942100 e 942110 (entrambe SQL injection detection) solo per le richieste dirette a /api/courses, non per tutto il sito. Per il cliente ho configurato 14 whitelist chirurgiche che hanno eliminato il 98% dei falsi positivi senza ridurre la copertura generale delle regole.
Fase 4 - Attivazione blocking (settimana 4). Dopo che il tuning è completo e l'audit log mostra zero (o quasi zero) falsi positivi, si passa a SecRuleEngine On - le regole ora bloccano effettivamente le richieste malevole. Durante la prima settimana di blocking mode, monitoring intensivo per catturare eventuali falsi positivi emergenti.
Le regole CRS più utili per applicazioni PHP: SQL injection, XSS, path traversal
OWASP CRS include circa 200 regole distribuite in famiglie tematiche. Le famiglie più rilevanti per applicazioni PHP moderne sono cinque.
REQUEST-942-APPLICATION-ATTACK-SQLI: detection di SQL injection. Regole che identificano pattern sintattici classici (UNION SELECT, OR 1=1, DROP TABLE), funzioni MySQL sospette (LOAD_FILE, INTO OUTFILE), tentativi di escape ('; --). Queste regole catturano la stragrande maggioranza di attacchi automatizzati tramite sqlmap o scanner simili. Sul cliente piemontese, 380 tentativi di SQL injection sono stati bloccati in 30 giorni.
REQUEST-941-APPLICATION-ATTACK-XSS: detection di cross-site scripting. Regole che identificano JavaScript injection nei parametri di input (<script>, javascript:, event handlers come onerror=), tentativi di bypass via encoding. Catturano principalmente attacchi contro campi di commento, profili utente, aree di testo libero.
REQUEST-930-APPLICATION-ATTACK-LFI: detection di Local File Inclusion. Regole che identificano tentativi di leggere file sensibili del server (/etc/passwd, ../../../../etc/shadow, PHP://filter). Rilevanti su applicazioni PHP legacy con pattern di include dinamico.
REQUEST-931-APPLICATION-ATTACK-RFI: detection di Remote File Inclusion. Simile a LFI ma per inclusione di file da URL remoti. Meno comune nelle applicazioni PHP moderne (funzioni pericolose come allow_url_include sono disabilitate di default) ma ancora rilevante.
REQUEST-932-APPLICATION-ATTACK-RCE: detection di Remote Code Execution. Regole che identificano tentativi di eseguire comandi shell tramite parametri HTTP (; ls -la, backtick commands, eval chains). Molto rilevante su applicazioni con funzionalità di preview/export che invocano binari di sistema.
Il pattern di SQL injection ricorrente che vedo è quello che ho descritto nel mio articolo sulla SQL injection nel 2025 e come prevenirla definitivamente per PMI, dove WAF e prevenzione applicativa agiscono come layer di difesa complementari.
Paranoia level: quando essere più restrittivi
OWASP CRS introduce il concetto di paranoia level - una scala da 1 a 4 che definisce quanto aggressive siano le regole attivate. Paranoia 1 (default) attiva solo regole con alta confidenza, pochissimi falsi positivi, copertura base. Paranoia 2 aggiunge regole più sensibili, alcuni falsi positivi attesi, copertura estesa. Paranoia 3 attiva regole molto sensibili, falsi positivi frequenti da gestire via tuning, protezione elevata. Paranoia 4 attiva tutte le regole, protezione massima ma richiede tuning estensivo.
La mia regola operativa è: paranoia 1 di default, paranoia 2 per applicazioni con dati sensibili (finanziario, sanitario, legale). Paranoia 3 e 4 sono per contesti enterprise con team dedicati alla gestione WAF continua - non per PMI dove il tuning extra costa più del beneficio aggiunto.
Monitoring e alert: Grafana + alert su anomalie
Il WAF produce un volume significativo di log - tipicamente 1-10 MB al giorno di ModSecurity audit log anche su applicazioni medie, con picchi durante tentativi di attacco concentrati. Questi log sono preziosi ma ingestibili manualmente. Il pattern operativo che applico include integration con piattaforma di log centralizzata (Loki + Grafana per PMI, Splunk o ElasticSearch per enterprise) e dashboard dedicato.
Le metriche chiave che espongo su Grafana sono: numero totale di richieste bloccate per ora (baseline + anomalie), top 10 regole CRS che scattano (permette di vedere pattern), distribuzione geografica degli IP sorgente di attacco, distribuzione per target path (quali aree dell'applicazione attirano più attacchi). Gli alert automatici si attivano per: burst di attacchi da singolo IP (possibile preparazione di exploit), pattern di probing su path nuovi (possibile scoperta di vulnerabilità), cambiamento drammatico del profilo di attacco (possibile campagna coordinata).
L'integration con alert Slack permette al team di sicurezza di reagire rapidamente a eventi critici senza dover monitorare il dashboard continuamente. Il pattern si integra con i principi di monitoring operativo che ho descritto nel mio articolo sul task scheduling robusto in Laravel con Horizon, dove observability continuous è un pilastro operativo.
Rate limiting e bot detection come layer complementare
ModSecurity con CRS offre detection di attack pattern ma non è il tool migliore per due categorie specifiche: rate limiting (limitare richieste/secondo per IP) e bot detection (identificare traffico automatizzato legittimo vs malevolo). Per queste categorie uso tool complementari.
Per rate limiting uso il modulo Nginx limit_req nativo, configurato con zone specifiche per endpoint critici (login, API pubbliche, form submission). La configurazione è minimale:
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
limit_req_zone $binary_remote_addr zone=api:10m rate=60r/m;
location /login {
limit_req zone=login burst=10 nodelay;
# ...
}Questo limita i tentativi di login a 5/minuto per IP, con burst fino a 10 - sufficiente per uso legittimo, insufficiente per brute force automation.
Per bot detection sofisticata su applicazioni con traffic volume significativo, ho usato Cloudflare (con piano Pro+) come proxy davanti a Nginx. Cloudflare offre bot detection avanzata con fingerprinting e machine learning che nessun WAF self-hosted può eguagliare per efficacia. Il trade-off è lock-in verso Cloudflare e costo ricorrente - accettabile per applicazioni con traffic significativo, over-engineering per PMI piccole.
Il risultato finale dell'intervento sul cliente piemontese dopo 6 mesi di operatività in blocking mode è stato il seguente. Richieste malevole bloccate automaticamente: 8.400 in 6 mesi, in gran parte da scanner automatici (sqlmap 45%, nikto 22%, nuclei 15%, altri 18%). Zero attacchi riusciti sulla piattaforma nel periodo (confermato dai log applicativi). Falsi positivi bloccati su traffico legittimo: 3 incidenti isolati in 6 mesi, tutti gestiti con aggiornamento delle whitelist entro 24 ore. Alert Slack generati dal WAF: circa 15-20 al giorno, gestibili dal team senza fatigue. Tempo di gestione operativa del WAF: circa 1 ora a settimana per review dei log e tuning incrementale. Costo consulenziale iniziale: 3.200 euro. Zero costo operativo ricorrente.
Se gestisci un'applicazione PHP pubblica con dati sensibili e non hai ancora un WAF in produzione, l'introduzione di ModSecurity con OWASP CRS è uno degli interventi difensivi con il miglior rapporto costo/beneficio disponibile - è open source, gira su Nginx già presente, e dopo tuning iniziale richiede manutenzione minimale. Se vuoi confrontarti sul tuo caso specifico con una proposta di implementazione calibrata sulla tua applicazione, contattami per una consulenza preliminare: in una sessione di analisi guidata produciamo insieme una mappatura del traffico attuale, un piano di rollout con fasi di audit e tuning specifiche al tuo dominio, e una configurazione ModSecurity pronta per deploy produzione.