Se gestisci decine di siti WordPress per conto di clienti, la sicurezza non è un’opzione: è infrastruttura critica. Una singola violazione su un sito può compromettere l’intera reputazione della tua agenzia. In questo articolo vediamo come abbiamo strutturato il hardening WordPress in AgencyPilot per gestire la sicurezza di oltre 50 siti in produzione, con monitoraggio centralizzato e risposta agli incidenti sotto i 5 minuti.
Perché la Sicurezza WordPress è Diversa per le Agenzie
Gestire la sicurezza di un singolo sito è un conto. Gestirla a scala — decine o centinaia di installazioni WordPress con clienti diversi, plugin diversi, configurazioni diverse — richiede un approccio sistematico che la maggior parte degli strumenti sul mercato non copre adeguatamente.
Il problema principale non è la tecnologia: è il volume. Quando hai 50 siti da monitorare, non puoi permetterti di controllare manualmente ogni log, ogni update, ogni tentativo di accesso fallito. Hai bisogno di automazione intelligente che ti avvisi solo quando c’è davvero qualcosa da fare.
Nella nostra esperienza con clienti nella Pubblica Amministrazione italiana — ASL, comuni, enti regionali — abbiamo imparato che la sicurezza deve essere:
- Preventiva: hardening configurato prima che avvenga qualsiasi incidente
- Centralizzata: visibilità su tutti i siti da un’unica dashboard
- Automatizzata: alert in tempo reale, non report settimanali
- Documentata: ogni azione tracciata per audit e conformità
Vediamo come implementare ciascuno di questi livelli.
Livello 1: Hardening di Base (Non Negoziabile)
Questi sono i primi interventi da eseguire su ogni installazione WordPress, prima di installare qualsiasi plugin di sicurezza. Senza questa base, qualsiasi strumento sopra è costruito sulla sabbia.
Protezione del file wp-config.php
Il wp-config.php contiene le credenziali del database e le chiavi di sicurezza. Va spostato fuori dalla webroot o protetto via .htaccess:
# Blocca accesso diretto a wp-config.php
<files wp-config.php>
order allow,deny
deny from all
</files>
# Blocca anche wp-config.php.bak e varianti
<FilesMatch "wp-config.*">
Order allow,deny
Deny from all
</FilesMatch>
Disabilitare XML-RPC (se non serve)
XML-RPC è il vettore più sfruttato per attacchi brute-force amplificati. Se non usi Jetpack o app mobile WordPress, disabilitalo completamente:
# In .htaccess
<Files xmlrpc.php>
Order Deny,Allow
Deny from all
</Files>
Oppure via functions.php:
// Disabilita XML-RPC completamente
add_filter('xmlrpc_enabled', '__return_false');
// Rimuove anche il link dall'header
remove_action('wp_head', 'rsd_link');
remove_action('wp_head', 'wlwmanifest_link');
Nascondere la versione WordPress
Esporre la versione WordPress nell’HTML facilita attacchi mirati. Rimuovila:
// Rimuove versione WordPress dall'header e dai link assets
remove_action('wp_head', 'wp_generator');
add_filter('the_generator', '__return_empty_string');
// Rimuove versione dagli URL degli asset (CSS/JS)
function agp_remove_version_scripts_styles($src) {
if (strpos($src, 'ver=') !== false) {
$src = remove_query_arg('ver', $src);
}
return $src;
}
add_filter('style_loader_src', 'agp_remove_version_scripts_styles', 9999);
add_filter('script_loader_src', 'agp_remove_version_scripts_styles', 9999);
Limitare i tentativi di login
WordPress di default non limita i tentativi di autenticazione falliti. Con il plugin Limit Login Attempts Reloaded o tramite configurazione Nginx/Apache:
# Nginx: limite di 10 richieste/minuto sull'endpoint di login
limit_req_zone $binary_remote_addr zone=wp_login:10m rate=10r/m;
server {
location = /wp-login.php {
limit_req zone=wp_login burst=3 nodelay;
include fastcgi_params;
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
Livello 2: Aggiornamenti Automatici Intelligenti
Il 60% delle violazioni WordPress sono dovute a plugin e temi non aggiornati con vulnerabilità note (fonte: WPScan Statistics). La soluzione non è “aggiornare sempre tutto automaticamente” — questo rompe i siti — ma aggiornare in modo selettivo e sicuro.
Strategia di aggiornamento a tre livelli
| Tipo | Policy | Motivo |
|---|---|---|
| Core WordPress (patch minori) | Auto-update immediato | Security fix critici, nessun breaking change |
| Core WordPress (major/minor) | Update manuale dopo test staging | Possibili breaking change alle API |
| Plugin (update di sicurezza) | Auto-update con notifica | CVE noti: priorità assoluta |
| Plugin (feature update) | Update manuale con backup preventivo | Rischio compatibilità |
| Temi | Update manuale sempre | Sovrascrive customizzazioni |
In AgencyPilot, questa logica è implementata automaticamente: il sistema controlla il WPScan Vulnerability Database e applica solo gli aggiornamenti con CVE associati, inviando un report al completamento. Per approfondire come gestiamo l’automazione degli update, vedi il nostro articolo sull’automazione della gestione WordPress.
Backup preventivo agli aggiornamenti
Ogni update di plugin critico deve avere un backup snapshot immediato prima dell’esecuzione. Il flusso che usiamo:
- Trigger WP-CLI:
wp plugin list --update=available --format=json - Per ogni plugin con CVE nel database: snapshot del database + file sito
- Esecuzione update:
wp plugin update {plugin-slug} - Health check automatico post-update (HTTP 200, caricamento homepage)
- Se health check fallisce: rollback automatico al snapshot
Questo processo riduce quasi a zero i siti “rotti per un update”. Per la strategia di backup completa, consulta la nostra guida su backup WordPress e disaster recovery.
Livello 3: Monitoraggio Centralizzato della Sicurezza
Il monitoraggio reattivo (ti accorgi del problema quando il cliente ti chiama) è inaccettabile per un’agenzia professionale. Il monitoraggio proattivo richiede la raccolta centralizzata di eventi di sicurezza da tutti i siti.
Cosa monitorare (e cosa ignorare)
Non tutti gli eventi di sicurezza meritano attenzione immediata. Classificazione per priorità:
| Evento | Priorità | Azione |
|---|---|---|
| Admin password cambiata | 🔴 Critica | Alert immediato + verifica |
| Nuovo utente ruolo Administrator | 🔴 Critica | Alert immediato + verifica |
| File PHP modificato fuori dal ciclo di deploy | 🔴 Critica | Alert immediato + quarantena |
| Plugin/tema sconosciuto installato | 🟠 Alta | Alert entro 5 minuti |
| Brute force login (>50 tentativi/ora) | 🟠 Alta | Block IP + notifica |
| Login admin da IP insolito | 🟡 Media | Log + notifica |
| Singoli tentativi di login falliti | ⚪ Bassa | Log senza notifica |
Integrazione con WP Activity Log
Il plugin WP Activity Log (WP Activity Log by WPWhiteSecurity) genera eventi strutturati per ogni azione nel sito. Li esportiamo via REST API al nostro sistema centrale:
# Recupera eventi delle ultime 24 ore via WP-CLI
wp wsal get-logs --format=json --date-from="$(date -d '24 hours ago' +%Y-%m-%d)" \
| jq '.[] | select(.severity == "critical" or .severity == "high")'
In AgencyPilot, questo polling avviene ogni 5 minuti per tutti i siti monitorati. Gli eventi vengono normalizzati e inviati al motore di correlazione che applica le regole di priorità della tabella sopra.
Scan delle vulnerabilità con WPScan
WPScan è lo standard de facto per l’audit di sicurezza WordPress. Con il piano API gratuito (25 richieste/giorno) puoi scansionare ogni sito giornalmente:
#!/bin/bash
# scan-security.sh — eseguito ogni notte alle 02:00
SITES_FILE="/etc/agencypilot/sites.txt"
WPSCAN_API_TOKEN="${WPSCAN_API_TOKEN}"
REPORT_DIR="/var/log/agencypilot/security"
while IFS= read -r site_url; do
site_name=$(echo "$site_url" | sed 's|https://||;s|/.*||')
wpscan \
--url "$site_url" \
--api-token "$WPSCAN_API_TOKEN" \
--enumerate vp,vt,u \
--plugins-detection aggressive \
--format json \
--output "${REPORT_DIR}/${site_name}-$(date +%Y%m%d).json" \
2>/dev/null
# Conta vulnerabilità trovate
vuln_count=$(jq '.vulnerabilities | length' "${REPORT_DIR}/${site_name}-$(date +%Y%m%d).json" 2>/dev/null || echo 0)
if [ "$vuln_count" -gt 0 ]; then
# Invia alert al sistema centrale
curl -s -X POST "${AGENCYPILOT_WEBHOOK}" \
-H "Content-Type: application/json" \
-d "{\"site\": \"$site_url\", \"vulnerabilities\": $vuln_count, \"report\": \"${REPORT_DIR}/${site_name}-$(date +%Y%m%d).json\"}"
fi
done < "$SITES_FILE"
Livello 4: Risposta agli Incidenti (Incident Response)
Avere un piano di risposta agli incidenti scritto prima che accada qualcosa è la differenza tra un'agenzia professionale e una che improvvisa sotto stress.
Playbook: sito compromesso
Questo è il flusso che seguiamo quando riileviamo un potenziale compromesso:
- Isolamento immediato: porta il sito in modalità manutenzione, blocca traffico in uscita dal server
- Snapshot forense: copia del filesystem e database nello stato attuale (non sovrascrivere le prove)
- Identificazione del vettore: analisi dei log Apache/Nginx per la prima richiesta anomala
- Rimozione del codice malevolo: confronto con backup pulito, rimozione file aggiunti/modificati
- Cambio credenziali: tutti gli account admin, chiavi API, password database
- Hardening post-incidente: patch della vulnerabilità sfruttata
- Ripristino graduale: riattivazione con monitoraggio intensivo per 48 ore
- Report al cliente: documentazione completa dell'incidente, azioni intraprese, misure preventive
Identificare file malevoli rapidamente
Quando un sito è compromesso, il primo passo è trovare i file aggiunti o modificati. WP-CLI e find sono i tuoi migliori alleati:
# File PHP modificati nelle ultime 48 ore (esclude cartelle normali di update)
find /var/www/html/sito-cliente -name "*.php" -newer /var/www/html/sito-cliente/wp-config.php \
-not -path "*/wp-content/cache/*" \
-not -path "*/wp-content/uploads/*" \
-ls
# Cerca pattern comuni di malware nei file PHP
grep -r --include="*.php" \
-e "eval(base64_decode" \
-e "system(\$_" \
-e "exec(\$_" \
-e "passthru(\$_" \
-e "shell_exec(\$_" \
/var/www/html/sito-cliente/ 2>/dev/null
Per siti WordPress, il plugin Wordfence con scan malware attivo può rilevare automaticamente le modifiche rispetto ai file originali del core e dei plugin.
Livello 5: Sicurezza dell'Infrastruttura Server
La sicurezza WordPress non esiste in isolamento: dipende dall'infrastruttura sottostante. Questi sono i punti critici lato server che spesso vengono trascurati.
Separazione dei siti (isolamento degli utenti)
Su un server condiviso tra più siti WordPress, ogni sito deve girare con un utente PHP-FPM separato. Se un sito viene compromesso, l'attaccante non deve poter accedere agli altri:
# /etc/php/8.2/fpm/pool.d/sito-cliente1.conf
[sito-cliente1]
user = cliente1
group = cliente1
listen = /run/php/php8.2-fpm-cliente1.sock
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 5
Questa configurazione significa che ogni sito è isolato: cliente1 non può leggere i file di cliente2, anche se entrambi girano sullo stesso server Nginx.
Fail2Ban per WordPress
Fail2Ban monitora i log e banna automaticamente gli IP che mostrano comportamenti malevoli. Configurazione specifica per WordPress:
# /etc/fail2ban/filter.d/wordpress.conf
[Definition]
failregex = ^<HOST> .* "POST .*wp-login.php HTTP.* 200
^<HOST> .* "POST .*xmlrpc.php HTTP.* 200
ignoreregex =
# /etc/fail2ban/jail.local
[wordpress]
enabled = true
port = http,https
filter = wordpress
logpath = /var/log/nginx/access.log
maxretry = 5
bantime = 86400
findtime = 600
Header HTTP di sicurezza
I Content Security Policy e gli altri header di sicurezza proteggono i visitatori del sito da attacchi XSS e clickjacking. Configurazione Nginx raccomandata:
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# CSP base — da personalizzare per ogni sito
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:;" always;
Per i siti della PA italiana, questi header sono anche richiesti dalle Linee Guida AGID per la sicurezza web, che costituiscono un obbligo normativo per gli enti pubblici.
Come AgencyPilot Centralizza la Sicurezza
Tutti i livelli descritti sopra richiedono configurazione e manutenzione su ogni sito. Quando gestisci decine di installazioni, questo diventa un lavoro a tempo pieno. È esattamente il problema che abbiamo risolto con AgencyPilot.
La dashboard di sicurezza di AgencyPilot ti mostra in una schermata:
- Stato degli aggiornamenti di sicurezza per tutti i siti
- Vulnerabilità attive rilevate da WPScan (CVE con CVSS score)
- Tentativi di intrusione bloccati nelle ultime 24 ore
- Siti con configurazioni di hardening incomplete
- Timeline degli eventi critici di sicurezza
Per approfondire come gestiamo la performance insieme alla sicurezza, vedi la nostra guida su ottimizzazione performance WordPress per agenzie. E per la gestione complessiva del parco siti, consulta gestione WordPress multi-sito per agenzie.
Compliance e Documentazione per i Clienti
Per le agenzie che lavorano con la PA italiana o con aziende soggette a normative (GDPR, NIS2), la documentazione della sicurezza è obbligatoria, non opzionale.
Il Regolamento NIS2 (recepito in Italia con D.Lgs. 138/2024) impone alle organizzazioni di medie e grandi dimensioni di documentare le misure di sicurezza adottate e di notificare gli incidenti significativi entro 24 ore. Questo include anche i fornitori di servizi IT — come le agenzie web.
Il minimo documentale che ogni agenzia deve mantenere per sito:
- Inventario versioni (WordPress, PHP, plugin attivi)
- Log degli aggiornamenti applicati con data
- Registro degli incidenti (anche quelli minori)
- Prova di esecuzione dei backup con verifica di ripristino
- Configurazione delle misure di hardening attive
In AgencyPilot, questo registro viene generato automaticamente e può essere esportato in PDF per i clienti che lo richiedono durante audit.
FAQ — Sicurezza WordPress per Agenzie
Quale plugin di sicurezza WordPress è il migliore per le agenzie?
Non esiste "il migliore" in assoluto. Per agenzie con molti siti, la combinazione più efficace è: Wordfence (firewall + malware scan), WP Activity Log (audit trail), e Limit Login Attempts Reloaded (protezione brute force). Per siti PA italiani, aggiungi WTYCZKA o plugin specifici per conformità AGID. Evita di installare più firewall plugin in conflitto.
Ogni quanto devo fare uno scan di sicurezza WordPress?
Per siti in produzione con traffico attivo: scan automatico giornaliero con WPScan, monitoring continuo dei log. Per siti a basso traffico: scan settimanale è sufficiente. Dopo ogni aggiornamento di plugin major: scan immediato. Dopo qualsiasi incidente: scan forense completo prima del ripristino.
Come faccio a sapere se un sito WordPress è stato compromesso?
I segnali più comuni: file PHP modificati di recente fuori dai cicli di update, nuovi utenti admin non autorizzati, redirect anomali su alcune pagine, codice eval(base64_decode nei file PHP, warning di Google Safe Browsing. Il monitoraggio proattivo con WP Activity Log rileva la maggior parte di questi eventi prima che diventino un problema visibile.
La sicurezza WordPress è responsabilità dell'agenzia o del cliente?
Dipende dal contratto. Nella pratica, se l'agenzia gestisce il sito (hosting incluso), la responsabilità operativa è dell'agenzia. Se il cliente gestisce hosting proprio con l'agenzia che sviluppa solo, la responsabilità è condivisa. Raccomandazione: definire esplicitamente nel contratto di servizio cosa copre la manutenzione sicurezza, con SLA documentati.
Quanto costa implementare un sistema di sicurezza serio per 50 siti?
Con tool open source (WPScan free tier, Fail2Ban, configurazione Nginx, WP-CLI): il costo principale è il tempo di implementazione, circa 40-60 ore per il setup iniziale. Con tool commerciali (Wordfence Premium, ManageWP Security): circa €150-300/mese per 50 siti. Con AgencyPilot (sicurezza integrata nella piattaforma): incluso nel piano agenzia senza costo addizionale.