Audit di sicurezza per applicazioni PHP legacy: una guida pratica per identificare e risolvere le vulnerabilità
A novembre 2024, durante un subentro su un gestionale PHP 7.0 di una PMI veneta - azienda metalmeccanica con 30 dipendenti, gestionale custom per ordini, produzione e fatturazione - ho condotto un audit di sicurezza che ha rivelato un quadro preoccupante: 14 endpoint con SQL injection diretta (variabili $_GET concatenate nelle query MySQL senza alcun escaping), 23 punti dove l'output utente veniva stampato senza htmlspecialchars (XSS riflesso), sessioni senza session_regenerate_id dopo il login (session fixation), zero protezione CSRF su qualsiasi form, e un file phpinfo.php nella webroot accessibile a chiunque che mostrava tutte le variabili d'ambiente, incluse le credenziali del database. Il gestionale era in produzione da sette anni e nessuno aveva mai condotto un audit di sicurezza.
Secondo l'OWASP Top 10 2021 - la classificazione di riferimento delle vulnerabilità più critiche nelle applicazioni web - i problemi trovati coprivano le prime tre categorie: A01 Broken Access Control, A03 Injection e A07 Cross-Site Scripting. Il gestionale era esposto a internet senza WAF, senza rate limiting e senza header di sicurezza. Qualsiasi attaccante con competenze anche basiche avrebbe potuto estrarre l'intero database con un singolo parametro URL manipolato.
In tre giorni di audit e due settimane di remediation ho chiuso tutte le 37 vulnerabilità critiche senza interrompere l'operatività del gestionale. In questo articolo ti racconto il metodo - lo stesso che applico a ogni audit di sicurezza su applicazioni PHP legacy.
Stai cercando un Consulente Informatico esperto per un audit di sicurezza del tuo applicativo PHP? Nel mio profilo professionale trovi l'esperienza concreta su vulnerability assessment, penetration testing e hardening di applicazioni legacy. Contattami per una consulenza diretta.
Come si conduce un audit di sicurezza su PHP legacy in 3 giorni?
L'audit segue tre fasi: discovery automatizzata (giorno 1), analisi manuale dei risultati (giorno 2), e classificazione con piano di remediation (giorno 3). L'obiettivo non è trovare ogni possibile vulnerabilità - è identificare le falle critiche che un attaccante può sfruttare con facilità.
Giorno 1: discovery automatizzata con grep e Psalm
Il primo strumento è il più semplice: grep. Le vulnerabilità SQL injection su codice PHP legacy hanno pattern riconoscibili - variabili $_GET, $_POST o $_REQUEST concatenate in stringhe SQL:
# SQL Injection: variabili utente concatenate in query
grep -rn "\$_GET\|\$_POST\|\$_REQUEST\|\$_COOKIE" . --include="*.php" | \
grep -i "query\|select\|insert\|update\|delete\|where" | wc -l
# XSS: output senza escaping
grep -rn "echo.*\$_GET\|echo.*\$_POST\|print.*\$_REQUEST" . --include="*.php" | wc -l
# File upload senza validazione
grep -rn "move_uploaded_file\|\$_FILES" . --include="*.php"
# Informazioni esposte
find . -name "phpinfo.php" -o -name "info.php" -o -name "test.php"
find . -name "*.sql" -o -name "*.sql.gz" -o -name "*.bak"Per un'analisi più sofisticata, Psalm con taint analysis è lo strumento più potente disponibile gratuitamente. Psalm traccia il flusso dei dati dall'input utente ($_GET, $_POST) attraverso tutto il codice fino ai "sink" pericolosi (query SQL, echo, header, include). Se trova un percorso non sanitizzato tra un source e un sink, segnala una vulnerabilità con il flusso completo dei dati:
# Installare Psalm
composer require --dev vimeo/psalm
# Inizializzare la configurazione
vendor/bin/psalm --init
# Eseguire l'analisi di sicurezza con taint analysis
vendor/bin/psalm --taint-analysisL'output di Psalm è preciso: indica il file, la riga, il tipo di vulnerabilità (SQLi, XSS, command injection, file inclusion) e il percorso del dato dall'input utente al sink pericoloso. Nel caso veneto, Psalm ha trovato 11 delle 14 SQL injection (le altre 3 erano in file che usavano mysql_query - funzioni troppo vecchie per essere tracciate dal type system di Psalm) e 19 dei 23 punti XSS.
Giorno 2: analisi manuale e verifica
I risultati automatizzati vanno verificati manualmente - sia per eliminare i falsi positivi sia per trovare le vulnerabilità che gli strumenti automatici non rilevano. La verifica manuale che conduco:
Per ogni SQL injection segnalata, verifico se c'è un percorso realmente sfruttabile dall'esterno. Una query con variabile concatenata che viene solo da una sessione autenticata è meno critica di una che accetta input da $_GET senza autenticazione. Per ogni XSS, verifico se l'output è in un contesto HTML dove JavaScript può essere eseguito - un echo dentro un attributo value="" è sfruttabile diversamente da un echo dentro un commento HTML.
L'OWASP ZAP (Zed Attack Proxy) completa l'analisi con uno scan dinamico - uno scanner che invia richieste reali all'applicazione e verifica le risposte per identificare vulnerabilità che l'analisi statica non può trovare (come problemi di configurazione del web server, header mancanti, cookie insicuri):
# OWASP ZAP spider + active scan (su ambiente di staging, MAI in produzione)
zap-cli spider http://staging.esempio.it
zap-cli active-scan http://staging.esempio.it
zap-cli report -o zap-report.html -f htmlGiorno 3: classificazione e piano di remediation
Le vulnerabilità trovate vanno classificate per severità e sfruttabilità. Il sistema che uso è il CVSS (Common Vulnerability Scoring System) semplificato in tre livelli:
- Critico (fix immediato): SQL injection raggiungibile senza autenticazione, RCE, file upload senza validazione, credenziali esposte (
phpinfo.php). Fix entro 48 ore. - Alto (fix entro 1 settimana): XSS riflesso su pagine pubbliche, sessioni senza regenerate, assenza CSRF su form critici (cambio password, ordini).
- Medio (fix entro 1 mese): XSS stored in pannello admin, header di sicurezza mancanti, configurazioni subottimali.
Nel caso veneto, il piano di remediation ha prodotto 8 interventi critici (chiusura SQL injection e rimozione phpinfo.php), 12 interventi alti (XSS, sessioni, CSRF) e 17 interventi medi.
Le correzioni: da SQL injection a prepared statement in un pomeriggio
La correzione delle SQL injection su codice legacy PHP è meccanica ma richiede attenzione: ogni query con variabile concatenata deve essere convertita a prepared statement. Su un gestionale con 14 endpoint vulnerabili, il lavoro richiede un pomeriggio:
<?php
// PRIMA: SQL injection diretta
$id = $_GET['id'];
$result = mysql_query("SELECT * FROM orders WHERE id = $id");
// DOPO: prepared statement con PDO
$stmt = $pdo->prepare("SELECT * FROM orders WHERE id = :id");
$stmt->execute(['id' => $_GET['id']]);
$result = $stmt->fetchAll();Per il XSS, la regola è htmlspecialchars() su qualsiasi output che include dati variabili:
<?php
// PRIMA: XSS riflesso
echo "<h1>Ordine " . $_GET['num'] . "</h1>";
// DOPO: output sanitizzato
echo "<h1>Ordine " . htmlspecialchars($_GET['num'], ENT_QUOTES, 'UTF-8') . "</h1>";Ho descritto il processo completo di refactoring sicuro - inclusa l'introduzione di test che verificano che le correzioni non rompano il funzionamento - nell'articolo sul refactoring di codice PHP legacy, e l'approccio al subentro su codebase senza documentazione che copre anche gli aspetti non-sicurezza.
L'audit di sicurezza su applicazioni PHP legacy non è un lusso riservato alle enterprise - è una necessità per qualsiasi applicazione che gestisce dati di clienti, ordini o fatture. Il costo di tre giorni di audit e due settimane di remediation è una frazione del costo di un data breach - che secondo il GDPR può comportare sanzioni fino al 4% del fatturato globale, oltre al danno reputazionale e alla perdita di fiducia dei clienti. Se il tuo gestionale o e-commerce PHP non ha mai subìto un audit di sicurezza, la domanda non è se ci sono vulnerabilità - è quante ce ne sono e quanto sono gravi. Contattami per scoprirlo prima che lo scopra qualcun altro.