Nella nostra analisi forense passo per passo di un attacco in stile Magecart abbiamo sottolineato un punto che, col senno di poi, meritava un articolo dedicato: ripulire un negozio dal malware non equivale a mettere in sicurezza il negozio. Abbiamo rimosso lo skimmer, le web shell e i binari SUID-root da quel negozio PrestaShop 1.7.x — e poco dopo è stato colpito di nuovo. Non attraverso una backdoor che ci era sfuggita, ma dalla porta principale: una vulnerabilità a livello applicativo che la pulizia aveva contenuto, ma mai chiuso. Questo post è la procedura operativa che avremmo voluto applicare fin dall'inizio — come rafforzare un negozio PrestaShop che non puoi aggiornare ancora, in modo che la rimozione del payload chiuda anche il buco da cui è entrato.
Revisionato a giugno 2026. Scritto per negozi PrestaShop 1.7.x bloccati sotto un ramo supportato; i passaggi qui sotto sono misure temporanee che fanno guadagnare tempo per una migrazione a PrestaShop 8 o 9 (Symfony 6.4), non un sostituto della migrazione.
Se puoi aggiornare oggi a PrestaShop 8 o 9, fallo invece — è l'unica soluzione duratura, e tutto ciò che segue è una misura provvisoria. Ma per un negozio reale un aggiornamento è un progetto di settimane: compatibilità dei moduli, refactoring del tema, riconfigurazione dei pagamenti. La fase intermedia esiste davvero, e durante quella fase sei un bersaglio attivo. Questa è la guida avanzata, da gestione incidente, che affianca due guide di base — inizia dalla checklist di hardening della sicurezza PrestaShop per la base completa, oppure dalla guida in linguaggio semplice per titolari di negozi se non hai un profilo tecnico — poi torna qui per il livello che permette a un negozio non aggiornabile di restare difendibile.
1. Trova il vettore d'ingresso, non solo il payload
L'errore più costoso nella risposta agli incidenti è trattare il sintomo visibile come se fosse la ferita. Uno skimmer nel JavaScript del tema è il sintomo. La ferita è ciò che ha permesso a un aggressore di scrivere quel JavaScript, in primo luogo — una web shell, un modulo non patchato, un endpoint vulnerabile a injection. Rimuovi lo skimmer e lascia aperta la ferita, e hai comprato qualche giorno, non sicurezza.
Prima di rafforzare qualunque cosa, rispondi a una domanda con prove concrete: come è stato scritto codice su disco? Leggi i log di accesso intorno ai timestamp di creazione dei file del malware — usa il tempo di modifica dell'inode (ctime), non il tempo di modifica del file (mtime), perché gli aggressori retrodatano spesso mtime per nascondersi. Correla le richieste POST sospette, i parametri di query insoliti e le anomalie nella dimensione delle risposte con il momento in cui è comparso il payload. Se non puoi indicare il vettore d'ingresso, presumi che sia ancora aperto e considera ogni passaggio qui sotto come essenziale.
2. Applica patch virtuali alle vulnerabilità nominate davvero da log e advisory
I negozi PrestaShop 1.7.x sono stati colpiti da diverse vulnerabilità rese armi operative, che gli scanner automatici cercano di individuare. Se non puoi aggiornare, devi patchare direttamente i percorsi di codice specifici — una "patch virtuale" applicata ai tuoi file. Sono misure temporanee, quindi segui gli advisory upstream, ma chiudono le porte che vengono forzate attivamente. Parti dalle prove: identifica il modulo esatto vulnerabile o la versione core indicata dai tuoi log e dagli advisory pertinenti, e patcha quello. Una nota catena d'attacco del 2022 combinava qualunque SQL injection sfruttabile con l'esecuzione tramite cache MySQL di Smarty per trasformare l'accesso al database in esecuzione PHP — quindi lo schema da sorvegliare è un'injection che raggiunge il database abbinata a un difetto della cache che esegue codice. L'esempio della ricerca a faccette qui sotto è uno di questi vettori di injection, ma patcha la versione installata di ps_facetedsearch solo se è confermato che sia vulnerabile; la metà SQL-injection di quella catena può provenire altrettanto facilmente da altro codice core o da altri moduli.
2.1 La SQL injection della ricerca a faccette (se la versione installata è vulnerabile)
Alcune vecchie build del modulo ps_facetedsearch concatenano i valori dei filtri direttamente in una clausola SQL WHERE — una primitiva di blind SQL injection. Prima di toccarlo, verifica negli advisory che la versione in uso sia effettivamente interessata; non tutti i negozi 1.7.x hanno proprio questo difetto. Dove si applica, la correzione consiste nel far passare ogni valore derivato dalla richiesta attraverso l'escaping nativo di PrestaShop prima che raggiunga la query: valori stringa racchiusi in pSQL() e tra virgolette, intervalli numerici forzati con un cast (float). Il pattern vulnerabile aggiunge current($values) grezzo nella condizione; il pattern patchato aggiunge pSQL(current($values)) tra virgolette per le stringhe e un valore castato per i numeri. Se questa versione è confermata vulnerabile, questo è il lucchetto sulla porta principale. (Lo stesso modulo sotto carico da crawler è anche un rischio di denial-of-service; trattiamo separatamente l'angolo del flood su ?q=.)
2.2 La RCE della cache MySQL di Smarty (CVE-2022-36408)
Questa è la metà della catena che trasforma l'accesso al database in esecuzione di codice: la cache dei template Smarty basata su database può essere forzata a renderizzare PHP controllato dall'aggressore. La maggior parte dei negozi non la usa mai. Disabilitala del tutto in config/smarty.config.inc.php neutralizzando la condizione che carica SmartyCacheResourceMysql.php e imposta il tipo di cache su mysql — racchiudi quell'include in una condizione sempre falsa, così non potrà mai essere raggiunto. Poi conferma che il negozio stia effettivamente usando la cache su filesystem (l'impostazione PS_SMARTY_CACHING_TYPE dovrebbe indicare filesystem, sotto Parametri avanzati → Prestazioni). Una SQL injection da sola è grave; una SQL injection che può scrivere un template PHP in una cache ed eseguirlo è una compromissione completa. Chiudi entrambe le metà e la catena si spezza.
2.3 Moduli abbandonati e file che gli aggressori prendono di nuovo di mira
Rendi l'audit meccanico. Elenca i vecchi moduli, i file eseguibili nei percorsi media scrivibili e i PHP/JS modificati di recente prima di decidere che la pulizia è conclusa:
find modules -maxdepth 2 -type f -name '*.php' -mtime +730 -print
find img upload download -type f -name '*.php' -print
find . -type f \( -name '*.php' -o -name '*.js' \) -mtime -14 -print
I moduli vecchi e non mantenuti (la famiglia RevSlider è l'esempio classico) sono un vettore ricorrente. La correzione duratura è disinstallare e sostituire il modulo — fallo ovunque sia possibile. Se davvero non puoi rimuoverne uno perché il tema ne dipende, non negare in blocco il PHP diretto in tutta la cartella del modulo: molti moduli espongono legittimamente punti di ingresso diretti — script AJAX, URL cron, callback di pagamento, script legacy di validazione — e un Require all denied su ogni .php li romperebbe. Esegui invece prima l'audit delle rotte e degli endpoint del modulo, poi blocca solo gli specifici punti di ingresso noti e abbandonati che un aggressore prende di nuovo di mira, aggiungendo una regola .htaccess circoscritta (Require all denied) per quei singoli file. Poi blocca i punti di ingresso statici che un aggressore prende di mira dopo ogni pulizia — sui filesystem che rispettano gli attributi estesi (ext4, xfs, btrfs), rendi immutabili index.php e i tuoi file config con chattr +i. Una shell futura non può sovrascrivere un file immutabile senza prima eseguire chattr -i, che richiede privilegi root che non dovrebbe avere. Per l'insieme completo di regole per directory root e cartella admin su cui si basa questo passaggio, vedi le nostre regole .htaccess di sicurezza e prestazioni.
3. Blocca il runtime PHP — correttamente
Disabilitare le funzioni PHP che una shell usa per avviare processi del sistema operativo vale la pena, ma è il passaggio più spesso eseguito male, in un modo che sembra applicato ma non lo è. La lista di blocco che ti serve copre la famiglia delle funzioni che avviano processi: exec, system, shell_exec, passthru, popen, proc_open e le relative, più dl, show_source e phpinfo. Impostarla è la parte facile. Sono tre i dettagli che spesso vengono ignorati e che decidono se servirà davvero a qualcosa:
- disable_functions è PHP_INI_SYSTEM. Viene applicato solo dal php.ini di avvio dell'esatto SAPI che serve il sito. Su un server gestito da pannello (Plesk, cPanel, CloudPanel), il SAPI web è di solito uno specifico pool PHP-FPM o un'istanza Apache per versione — non il PHP CLI di sistema che trovi per primo dalla shell. Modifica il php.ini corretto e riavvia esattamente quel servizio, altrimenti la modifica non farà nulla pur sembrando completata.
- Controlla disable_functions dal SAPI giusto. ini_get('disable_functions') eseguito da CLI potrebbe non corrispondere a ciò che il SAPI web applica davvero — il PHP CLI e il tuo pool PHP-FPM web possono avere configurazioni diverse. L'unico test affidabile è reale, eseguito via web: uno script temporaneo con nome casuale, eliminato subito dopo, che chiama function_exists('exec') e tenta una chiamata effettiva disabilitata come @exec('id'), recuperato via HTTP. Fidati del risultato del tentativo, non di un valore di configurazione letto dal SAPI sbagliato.
- Non è un interruttore di spegnimento. Impedisce a una shell PHP di chiamare la shell del sistema operativo. Non neutralizza una shell PHP basata su eval, che può ancora leggere e scrivere file dentro open_basedir e parlare con il database usando le credenziali presenti in app/config/parameters.php. Trattala come un livello, non come la risposta definitiva — ed è esattamente per questo che il rilevamento (passaggio 5) e il WAF (passaggio 4) stanno al suo fianco, non al suo posto.
Una nota di compatibilità per non rompere gli upload: il core di PrestaShop tocca exec() in un paio di percorsi per immagini e rilevamento MIME, ma quei percorsi hanno fallback con finfo, quindi disabilitarla non rompe il caricamento delle immagini prodotto. Testa comunque gli upload delle immagini in staging prima di applicare la modifica in produzione.
4. Metti Cloudflare davanti — come livello di patch virtuale
Un WAF reverse-proxy è la cosa più vicina a una patch virtuale che puoi distribuire in pochi minuti: ispeziona le richieste prima che raggiungano il tuo codice vulnerabile. Ma merita quel ruolo solo se gli aggressori non possono aggirarlo.
4.1 Blocca l'origine su Cloudflare
Un WAF davanti a un'origine che risponde ancora sul proprio IP pubblico è opzionale dal punto di vista dell'aggressore — gli basta colpire direttamente l'IP e saltare il proxy. Limita il server in modo che solo Cloudflare possa raggiungere le porte 80 e 443: una catena iptables che accetta gli intervalli IPv4 e IPv6 pubblicati da Cloudflare (e localhost) su quelle porte e scarta tutto il resto. Il dettaglio che quasi tutti trascurano: il tuo DNS di posta rivela l'IP dell'origine. Un record MX, una voce SPF o un sottodominio mail. con nuvola grigia consegnano all'aggressore il tuo vero indirizzo — e il firewall sull'origine è ciò che rende innocua quella fuga per il web. Quindi fai passare ogni record web attraverso Cloudflare (nuvola arancione) e proteggi comunque l'origine con firewall. Lascia SSH, posta e porte del pannello sulle rispettive regole di accesso; il blocco del web non deve mai toccarle.
4.2 Regole WAF personalizzate — e la trappola delle estensioni
Con il piano Cloudflare Free hai cinque regole personalizzate e niente espressioni regolari — solo contains, eq, starts_with, ends_with e lower(). È sufficiente per un set di regole mirato:
- Blocca web shell e upload eseguibili: nomi di file di shell noti, chiamate dirette a PHP di moduli abbandonati e qualunque esecuzione PHP sotto directory di asset (/img/, /upload/, /themes/, /js/, /cache/) dove PHP non dovrebbe mai girare.
- Blocca i payload di injection: stringhe di path traversal, richieste a /etc/passwd, union select, information_schema, un tag di apertura PHP letterale, base64_decode e le loro varianti codificate più comuni.
- Blocca i file sensibili: /.git/, /.env, composer.json e i tuoi file di configurazione — non devono mai essere serviti al pubblico.
- Blocca i crawler molesti peggiori in base allo user-agent. Se il traffico da bot scadenti è un problema costante e non un episodio isolato, la nostra guida su come bloccare bot indesiderati e traffico non voluto va più a fondo di quanto possano fare cinque regole.
La trappola che ci ha colpiti: Apache spesso esegue più di .php. Una tipica riga AddType/AddHandler mappa .php3 .php4 .phtml all'handler PHP insieme a .php. Una regola che blocca .phtml e .phar ma dimentica .php3 e .php4 lascia spalancata una porta per l'upload di shell — abbiamo trovato esattamente quel vuoto nel nostro set di regole durante una revisione. Blocca ogni estensione che il server eseguirà davvero e controlla la configurazione reale di AddType/AddHandler invece di presumere il default.
4.3 Rate limiting: sappi cosa può e cosa non può fare il tuo piano
Il rate limiting è lo strumento giusto contro le ondate di crawler che martellano la ricerca a faccette. Ma devi essere preciso sul tuo piano, perché il piano Free è molto più limitato di quanto la documentazione lasci intendere a colpo d'occhio. La tabella qui sotto mostra la distinzione che decide se la tua difesa scatterà davvero:
| Funzionalità | Cloudflare Free | Pro (Advanced Rate Limiting) |
|---|---|---|
| Corrispondenza sul percorso URL | Sì | Sì |
| Corrispondenza sulla query string (es. faccette ?q=) | No | Sì |
| Corrispondenza su user-agent / IP sorgente | No | Sì |
| Azione | Solo blocco | Challenge gestita, blocco, log |
| Finestra di conteggio | Fissa | Configurabile |
La conseguenza pratica: con Free non puoi costruire una regola "limita i bot sugli URL a faccette ?q=", perché la query string è invisibile. Il meglio che Free ti dà è un limite grezzo di flood per IP sui percorsi non statici. Un vero rate limiting consapevole di query e user-agent, con challenge gestita invece di un blocco secco che rimbalzerebbe anche clienti reali, richiede il piano Pro. Per un negozio attivamente preso di mira, l'esposizione della ricerca a faccette di solito giustifica quell'upgrade — valutalo contro il costo del DoS che previene.
5. Aggiungi rilevamento — così il secondo round viene intercettato in ore, non giorni
L'hardening riduce le probabilità; non ti rende immune. La differenza tra un brutto pomeriggio e un incidente da notifica formale è quanto in fretta te ne accorgi. Il negozio del nostro caso studio aveva sottratto in silenzio i dati delle carte dei clienti per quasi due giorni prima che qualcuno controllasse. Mettici sopra degli occhi:
- Monitoraggio dell'integrità dei file (AIDE, Wazuh, OSSEC o anche un semplice cron con manifest hashato) sulla webroot, con avvisi per ogni file PHP nuovo o modificato che non provenga da un deploy. Nuovo PHP che compare fuori da una release è l'allarme a segnale più alto che puoi configurare.
- Scansioni programmate degli indicatori per i pattern degli attacchi reali — gli indicatori nella nostra anatomia di un attacco Magecart sono una lista di partenza già pronta — più qualunque nuovo file con estensione eseguibile che compaia nelle directory di asset o upload.
- Revisione degli eventi di sicurezza Cloudflare a cadenza regolare, non solo dopo un incidente. Il tuo WAF è anche un sensore, e registra i tentativi bloccati dalle tue regole: questo ti dice che cosa viene provato.
6. Il pannello di amministrazione è ancora una porta — chiudila anche tu

Un elenco di blocchi popolato per area: un blocco dell'intero sito, un blocco solo per il checkout e un blocco solo per il login, ciascuno con il proprio IP, tipo e motivo.
Tutto ciò che precede difende il front end pubblico. Non lasciare il back office senza hardening dando per scontato che gli aggressori arrivino solo dal catalogo: un login admin trapelato o forzato con brute force aggira del tutto le patch virtuali. Su un negozio non aggiornabile questo conta di più, non di meno, perché anche il modulo di login potrebbe eseguire codice vecchio. Come minimo, rinomina la directory admin, forza HTTPS ovunque (la nostra guida alla configurazione di SSL e HTTPS spiega come farlo in modo pulito) e aggiungi un secondo fattore e una vera policy password — il trattamento completo è in autenticazione a due fattori, policy password e sicurezza admin. Abbinalo al blocco a livello IP dei recidivi, che spieghiamo in ban IP per visitatori problematici.
7. La cura resta la migrazione
Tutto ciò che precede è defense-in-depth su software che non riceve più aggiornamenti di sicurezza. Aumenta il costo dell'attacco, chiude le porte sfruttate attivamente e ti fa guadagnare le settimane o i mesi necessari a una migrazione vera — ma compra tempo, non sicurezza. Il negozio del nostro caso studio lo dimostra esattamente: è stato ripulito, e lo stesso operatore è rientrato attraverso un difetto applicativo che la pulizia non aveva mai patchato. Pianifica il passaggio a PrestaShop 8 o 9, su infrastruttura pulita, con credenziali ruotate, come vera remediation. Considera l'hardening qui descritto come ciò che tiene acceso il negozio finché non ci arrivi.
Un livello in più che vale la pena aggiungere: una CSP in modalità report-only
Tutto ciò che precede prova a impedire che uno skimmer venga scritto su disco. Una Content-Security-Policy è il livello che limita i danni se uno riesce comunque a passare — vincola da dove il browser può caricare script e dove può inviare dati, che è precisamente il comportamento di cui uno skimmer di carte ha bisogno. Su un negozio non aggiornabile dovresti distribuirla prima in modalità report-only, così registra ciò che bloccherebbe senza rompere gli script del tema e dei moduli. Osserva i report per una settimana, aggiungi le origini legittime che il tema usa davvero, poi passa all'applicazione effettiva.
# Apache: start in report-only so nothing breaks while you learn your own origins.
# Tighten 'self'/allow-lists to your real theme + payment + analytics hosts, then
# switch the header name to Content-Security-Policy to enforce.
Header always set Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self' https://js.stripe.com; connect-src 'self' https://api.stripe.com; frame-src https://js.stripe.com; object-src 'none'; base-uri 'self'"
Il punto, per un negozio bloccato, è lo stesso di ogni altro passaggio qui: una CSP in modalità report-only trasforma un'iniezione silenziosa di skimmer in un'anomalia registrata che puoi intercettare in ore, e una CSP applicata può fermare del tutto la fase di esfiltrazione. Si abbina al rilevamento del passaggio 5 — il report di violazione della policy è esso stesso un allarme. Sostituisci gli host Stripe qui sopra con il gateway e gli strumenti analytics che il tuo negozio carica davvero; una allow-list troppo ampia vanifica lo scopo.
Domande frequenti
Il virtual patching è una correzione reale o rimanda solo l'inevitabile?
È una misura temporanea e devi trattarla come tale. Aumenta il costo dell'attacco e chiude le porte specifiche che vengono forzate, il che ti fa guadagnare le settimane o i mesi necessari a una migrazione reale — ma compra tempo, non sicurezza. Il negozio del nostro caso studio è stato ripulito e poi è stato violato di nuovo attraverso un difetto applicativo che la pulizia non aveva mai patchato. Pianifica il passaggio a PrestaShop 8 o 9 su infrastruttura pulita come vera remediation.
Ho disabilitato le funzioni PHP pericolose, ma una shell di test è partita lo stesso. Perché?
disable_functions è PHP_INI_SYSTEM — ha effetto solo nel php.ini di avvio dell'esatto SAPI che serve il sito, che su un server gestito da pannello è di solito uno specifico pool PHP-FPM, non il PHP CLI che raggiungi per primo dalla shell. Modifica il php.ini corretto, riavvia esattamente quel servizio e verifica tramite una richiesta HTTP reale invece che da CLI con ini_get(), che può leggere una configurazione diversa. La sezione 3 sopra mostra il test corretto.
Il piano gratuito di Cloudflare proteggerà un negozio vulnerabile?
In parte. Può bloccare web shell, payload di injection e file sensibili con cinque regole personalizzate, e vale la pena farlo. Quello che non può fare è fare match su query string o user-agent nel rate limiting — quindi una regola "limita i bot sugli URL a faccette ?q=" non è possibile su Free, perché la query string è invisibile. Per un negozio attivamente preso di mira, l'esposizione della ricerca a faccette di solito giustifica Pro.
Devo davvero mettere un firewall davanti all'origine se Cloudflare è già davanti?
Sì. Un WAF davanti a un'origine che risponde ancora sul proprio IP pubblico è opzionale dal punto di vista dell'aggressore — colpisce direttamente l'IP e salta il proxy. E il tuo DNS di posta (un record MX, SPF o un sottodominio mail. con nuvola grigia) rivela regolarmente quell'IP. Limita le porte 80 e 443 agli intervalli pubblicati da Cloudflare, così la fuga è innocua per il web; lascia SSH, posta e porte del pannello sulle rispettive regole.
Rendere i file immutabili con chattr rompe PrestaShop?
È sicuro sui file che non dovrebbero cambiare tra un deploy e l'altro — index.php e i tuoi file config — e funziona solo sui filesystem che rispettano gli attributi estesi (ext4, xfs, btrfs; alcune configurazioni con overlay di container e ZFS no). Una futura web shell non può sovrascrivere un file immutabile senza prima eseguire chattr -i, che richiede privilegi root che non dovrebbe avere. Non rendere immutabili in blocco le directory in cui PrestaShop scrive, come var/ o img/.
Continua a leggere
- Anatomia di un attacco in stile Magecart a un negozio PrestaShop 1.7.x — l'incidente che ogni passaggio qui serve a evitare che si ripeta.
- Hardening della sicurezza PrestaShop: la checklist completa — la base su cui costruisce questo livello avanzato.
- Risposta a una violazione dei dati: cosa fare se il tuo negozio viene hackerato — per quando stai leggendo dopo una violazione, non prima.
E se stai leggendo questo dopo una violazione invece che prima, cambia percorso: la nostra guida alla risposta a una violazione dei dati spiega cosa fare nelle prime ore, e l'anatomia dell'attacco che ha motivato ogni passaggio qui sopra mostra che cosa hai davvero di fronte.
Commenti
Ancora nessun commento. Sii il primo!
Sii il primo a fare una domanda o a condividere un feedback utile.
Lascia un commento
Condividi una domanda, un dettaglio di installazione o un feedback utile per un altro lettore.