L'OWASP (Open Web Application Security Project) da alcune settimane ha pubblicato un interessante articolo in cui vengono indicate le cinque maggiori pecche a livello di sicurezza in PHP. OWASP, di cui ci siamo già occupati in un articolo della sezione sicurezza, è un progetto che punta a creare una serie di strumenti e documenti che facilitino la scrittura di applicazioni web sicure. Per quanto riguarda il linguaggio PHP sono stati analizzati tutti i problemi riportati nella nota mailing list dedicata alla sicurezza Bugtraq ed è stata stilata una classifica dei cinque maggiori problemi di sicurezza. In questo articolo le passeremo in analisi e vedremo come evitare queste falle nelle nostre applicazioni web. Per una visione più completa rimandiamo anche alla Guida sicurezza di PHP pubblicata nella sezione PHP del nostro network.
Preliminari: le direttive e la versione di PHP
L'articolo inizia con una breve introduzione che puntualizza due concetti fondamentali riguardo PHP: l'utilizzo di safe_mode
per aumentare la sicurezza di PHP e l'utilizzo di addslashes
e magic_quotes
. Per quanto riguarda safe_mode
(che è una direttiva che può essere impostata dal file php.ini) è bene ricordare che il suo semplice utilizzo senza nessun altro accorgimento non rende minimamente sicuro il nostro sistema. La maggior parte delle restrizioni che vengono impostate abilitando il safe_mode
possono essere aggirate utilizzando script sviluppati in modo intelligente; per esempio molti software commerciali o di pubblico utilizzo hanno utilizzato hack appositi per aggirare delle protezioni della direttiva di configurazione safe_mode che avrebbero limitato il funzionamento dell'applicazione. Per quanto riguarda gli hoster dovrebbe essere fatta un po' più di attenzione allo studio del codice caricato sui server piuttosto che sperare che, abilitando safe_mode
, tutto sia sicuro e ben funzionante. A parte tutto questo è bene precisare che safe_mode
non è del tutto inutile: se ben strutturato ed integrato con l'applicazione può aumentare il grado di sicurezza del sistema.
Il secondo punto è legato all'utilizzo delle funzioni addslashes
e le funzioni/direttive magic_quotes_*
. Purtroppo, nonché sia consigliato ovunque l'utilizzo della funzione addslashes
, questa non previene da una delle cause maggiori degli attacchi alle applicazioni PHP: l'SQL Injection. Per prevenirle è essenziale non utilizzare addslashes
, disabilitare la direttiva magic_quotes_gpc
in tutte le installazioni ed appoggiarsi a qualche libreria nativa o esterna che abbia il supporto per accessi più sicuri al database. Viene fatto riferimento alla libreria PDO, che viene fornita di default da PHP 5.1 in avanti, ma è da specificare che attualmente non è per nulla stabile; in PHP 5.2 dovrebbero risolvere tutti questi problemi.
Molto importante è il riferimento che viene fatto alla versione di PHP da installare: per prevenire la maggior parte dei problemi e dei rischi di sicurezza (in seguito spiegheremo in modo più dettagliato questa affermazione) è necessario installare PHP 5.1 o superiori. Sfortunatamente gli hoster che supportano questa versione si contano sulla punta delle dita dato che molti preferiscono rimanere attaccati alla versione 4 per problemi di incompatibilità che in realtà si trasformano in problemi di sicurezza: se un software non è compatibile con PHP 5.1 allora vuol dire che ha grossi problemi di sicurezza che comunque andrebbero corretti.
PHP 4 non è sicuro, sia per via delle impostazioni standard del file php.ini sia per il fatto che non supporta PDO ed altre interfacce di accesso alle risorse molto sicure. Oltretutto il problema dell'incompatibilità si riduce del tutto quando ci si rende conto che tutte le applicazioni commerciali o molto importanti funzionano senza alcun problema su PHP 5, ed alcune di queste (come MediaWIKI) richiedono PHP 5 per poter funzionare. Ormai sono anni che viene utilizzato. Sarebbe ora di aggiornarsi.
Passiamo ora ad analizzare i cinque maggiori rischi di sicurezza.
P1 - Esecuzione remota del codice
L'esecuzione remota di codice è il problema di sicurezza più discusso ed affrontato dalla nascita di applicazioni che sfruttano l'inclusione ed esecuzione dinamica di moduli. I problemi legati all'esecuzione remota del codice sono normalmente causati da una insufficiente validazione dell'input dell'utente prima della chiamata di accesso alle risorse (come require
, include
, fopen
), che facilita enormemente la vita agli intrusi.
La direttiva allow_url_fopen
è abilitata di default ma, oltre a non essere utilizzata dalla maggior parte delle applicazioni, rischia di creare gravi problemi di sicurezza. Da non sottovalutare sono i permessi assegnati dagli hoster ai file ed alle cartelle di lavoro: troppo spesso capita di vedere situazioni in cui si hanno privilegi eccessivi che permettono di accedere ad una gamma troppo ampia di risorse rispetto alle esigenze. Tutte queste situazioni insieme rendono le applicazioni PHP sviluppate senza accortezza molto insicure.
Per capire se il nostro sistema è vulnerabile ad attacchi di questo tipo è necessario fare un'analisi meticolosa del codice che interagisce con le risorse di sistema. In queste situazioni bisogna assicurarsi che l'input dinamico che arriva alle funzioni sia validato e sicuro e che non ci siano situazioni in cui le funzioni accettano come input direttamente parametri provenienti dalle variabili superglobali come $_POST o $_GET. Tutto quello che proviene dall'esterno può essere insicuro e quindi dovrebbe essere sempre inteso come tale prima della validazione.
Le funzioni che se mal utilizzate possono portare problemi di esecuzione remota del codice sono, in linea di massima, quelle per accedere al filesystem (fopen
, mkdir
, unlink
), quelle per eseguire direttamente comandi del sistema operativo (come system
, popen
o l'operatore backtick
), la funzione eval
, le funzioni di inclusione che come parametri non hanno stringhe statiche e quelle che creano un'immagine partendo da un file iniziale (imagecreateform
*).
Per proteggersi da questo problema è necessario che sia gli hoster che gli sviluppatori prendano delle precauzioni: gli hoster dovrebbero disabilitare la direttiva allow_url_fopen
, abilitare safe_mode
, mettere delle restrizioni utilizzando la direttiva open_basedir
e strutturare l'ambiente in modo da fornire all'applicazione solamente l'accesso alle risorse necessarie. Gli sviluppatori invece dovrebbero per prima cosa analizzare interamente il codice per valutare dove potrebbero verificarsi problemi di sicurezza ed assicurarsi che i dati passati alle funzioni poco sicure siano sempre validati e, quando scrivono nuovo codice, limitare l'utilizzo improprio di queste funzioni.
P2 - Cross Site Scripting (XSS)
Il Cross Site Scripting (conosciuto anche come HTML Injection), è quell'attacco che permette l'esecuzione di codice JavaScript (o altro codice interpretabile dal client) non verificato e non previsto dall'applicazione oppure l'inserimento di contenuto non previsto all'interno delle proprie pagine HTML. Questo codice viene solitamente fatto eseguire aggiungendo dei tag HTML in posizioni in cui questi non erano previsti.
In PHP è possibile eseguire attacchi XSS in tre modi differenti: in primo luogo l'aggressore può mostrare un link che rimanda ad un sito Internet o ad una pagina con contenuto pericoloso. In secondo luogo è possibile che l'aggressore salvi dei dati nocivi direttamente nel database, utilizzando delle falle nel sistema di salvataggio dei dati, che vengono successivamente visualizzati alla vittima. Infine l'aggressore potrebbe spingere all'esecuzione di codice JavaScript direttamente nel client per modificare il DOM della pagina affinché operi in modo differente da quello che ci si aspetterebbe. Purtroppo non esiste alcun sistema interno a PHP per proteggersi da questo tipo di attacchi e quindi è strettamente necessario che si operi con attenzione quando si sviluppano applicazioni che hanno a che fare con la visualizzazione dell'input immesso dall'utente.
Per capire se un'applicazione può essere a rischio XSS è necessario per prima cosa verificare che la direttiva register_globals
sia disabilitata. In caso contrario l'applicazione avrebbe un grossissimo rischio di vulnerabilità, e sarà molto difficile operare affinché si sia sicuri che tutto funzioni a dovere. In secondo luogo bisognerebbe cercare tutte le zone in cui si visualizza direttamente l'input inserito dall'utente oppure quando si salvano dati affinché siano successivamente visualizzabili. In entrambi i casi è necessario assicurarsi che l'input dell'utente sia correttamente validato e che, ove non ammesso, non sia possibile utilizzare codice HTML di nessun genere. Per fare questo occorre sviluppare una libreria apposita che si occupi di filtrare l'input eliminando o quotando i tag non ammessi, oppure è possibile sfruttare le funzioni built-in di PHP (come htmlentities
) per eseguire in modo più generico ma più semplice queste operazioni. Una buona soluzione adottata dagli sviluppatori è quella che prevede l'eliminazione di tutti i tag HTML e l'utilizzo di un linguaggio specifico (come BBcode o il WikiFormatting) che verrà poi trasformato in HTML valido ed accettato.
Per proteggersi da questo tipo di attacchi è dunque necessario fare affidamento a sistemi che validino e filtrino l'input, disabilitare register_globals, recuperare l'input dell'utente dalla locazione corretta anziché appoggiarsi sulla variabile $_REQUEST
, effettuare la codifica degli URL visualizzati all'utente utilizzando urlencode
ed infine validare il codice JavaScript in modo che sia immune da attacchi di DOM injection.
Nella prima parte dell'articolo abbiamo affrontato due dei principali problemi di sicurezza legati a PHP, l'esecuzione remota di codice ed il Cross Site Scripting. In questa seconda parte analizzeremo gli ultimi tre problemi evidenziati dall'OWASP: SQL Injection, la configurazione di PHP, e gli attacchi al filesystem.
P3 - SQL Injection
L'SQL Injection è uno degli attacchi più vecchi e forse più conosciuti nell'ambito delle applicazioni web. Fortunatamente, essendo molto conosciuto e ben studiato, ci sono svariate tecniche che permettono di proteggersi e svariate librerie che svolgono da sole gran parte del lavoro di immunizzazione dal problema.
Come per tutti gli attacchi simili, è necessario validare l'input dell'utente prima di procedere con l'utilizzo dei dati; purtroppo solo la validazione non basta e spesso è necessario affidarsi a strumenti che quotino opportunamente le stringhe contenute nei dati affinché possano essere utilizzate all'interno delle nostre query SQL. PHP fornisce molte librerie per l'accesso ai database, e la maggior parte di queste forniscono questi strumenti di quoting: PDO, MySQLi e la libreria non nativa PEAR:DB. Se proprio non si dovesse avere la possibilità di utilizzare queste soluzioni (che sono di gran lunga superiori alle altre alternative) è necessario appoggiarsi alle funzioni di quoting delle stringhe come mysql_real_escape_string
o simili per altri database. Fino a PHP 4 la maggior parte del lavoro doveva essere fatta dal programmatore o dal librerie esterne. Con PHP 5.1 le librerie built-in si occupano automaticamente di queste operazioni rendendo il codice molto sicuro anche senza aver prestato particolare attenzione alla protezione verso le SQL Injection.
Purtroppo l'utilizzo di addslashes
è insufficiente e dovrebbe essere deprecato in favore delle soluzioni esposte precedentemente; allo stesso modo magic_quotes
dovrebbe essere disabilitato in modo che il codice sviluppato sia in grado di funzionare senza problemi anche su quei sistemi che hanno la direttiva disabilitata di default.
Per capire se le proprie applicazioni sono vulnerabili a questo tipo di attacchi è necessario trovare tutte quelle situazioni in cui le query SQL vengono composte partendo o utilizzando dati in input e assicurarsi che la query eseguita sia sempre sicura. Gli sviluppatori dovrebbero migrare interamente il loro codice a PHP 5.1 per poter sfruttare le librerie built-in che forniscono supporto contro le SQL Injection; se questo non è possibile ci si dovrebbe appoggiare a soluzioni esterne che emulino le stesse funzionalità (come PEAR:DB).
I dati in input che vengono utilizzati all'interno delle query dovrebbero comunque essere validati affinché seguano una determinata sintassi e rispondano ai limiti di lunghezza e tipo necessari e, nel caso estremo in cui non fosse possibile appoggiarsi ai layer sicuri di accesso ai DB, dovrebbero essere "quotati" con mysql_real_escape_string
o funzioni simili.
Gli hoster, dal canto loro, non possono prevenire le SQL Injection in nessun modo ma possono ridurre i problemi utilizzando PHP 5.1 e fornendo PDO ai loro clienti.
P4 - Configurazione di PHP
Anche se non è possibile considerarla come un attacco vero e proprio, la configurazione scorretta di PHP è uno dei maggiori motivi per cui si hanno falle di sicurezza nelle applicazioni web sviluppate con questo linguaggio. Il grosso problema è che molte opzioni relative alla sicurezza sono impostate in modo non corretto anche nella configurazione standard (quella più adottata dagli hoster) dando un falso senso di sicurezza quando in realtà i problemi possibili sono molti.
Purtroppo però non c'è nessun tipo di configurazione ritenuta globalmente sicura per PHP e questo crea non pochi problemi nella scelta di come operare sul proprio server. Le direttive che, direttamente o indirettamente, si riferiscono alla sicurezza sono le seguenti:
register_globals
: l'indicazione che debba assolutamente essere impostato adoff
è stato ripetuto in tutti i modi, in moltissime situazioni e da moltissime persone, ma pare che non tutti si siano ancora mossi in quella direzione;allow_url_fopen
: come indicato nello scorso articolo, questa direttiva è attiva di default ma dovrebbe essere disabilitata per prevenire attacchi di esecuzione remota di codice;magic_quotes_gpc
: dovrebbe essere disabilitata dato che le applicazioni dovrebbero essere progettate e sviluppate affinché si proteggano dalle SQL Injection senza l'ausilio del quoting automatico dell'input;magic_quotes_runtime
: fortunatamente questa direttiva è impostata adoff
e così dovrebbe essere mantenuta;safe_mode
: come specificato nell'introduzione allo scorso articolo,safe_mode
da solo non basta per rendere PHP sicuro, ma è già un buon inizio e per questo motivo la direttiva dovrebbe essere attivata e configurata correttamente;open_basedir
: vale lo stesso discorso disafe_mode
e quindi anche questa direttiva dovrebbe essere abilitata e configurata a dovere per limitare il movimento dell'applicazione solamente all'interno di un ambiente ristretto;
Una configurazione sicura e corretta è fondamentale, e dovrebbe essere scelta anche se questo dovesse portare ad incompatibilità nel software (che andrebbe ovviamente correttamente aggiornato). Dal punto di vista degli sviluppatori quindi si dovrebbe operare in modo che le applicazioni siano portate a PHP 5 (ove necessario) e funzionino con la configurazione sicura proposta precedentemente; sarebbe anche opportuno fornire script di installazione che controllino le impostazioni del file php.ini e notifichino eventuali configurazioni errate all'utente. L'hoster dovrebbe dal canto suo aggiornare la versione di PHP alla 5.1 e configurare correttamente il file php.ini. Se possibile si dovrebbe anche lasciare la possibilità di configurare alcune opzioni di PHP attraverso file .htaccess così da lasciare ai singolo sviluppatori delle piccole libertà su quelle impostazioni che non sono considerate universalmente corrette.
P5 - Attacchi al filesystem
L'ultimo degli attacchi analizzati dal progetto OWASP è relativo agli strumenti di accesso al filesystem che, se utilizzati in modo non corretto potrebbero portare a problemi particolarmente gravi come ad esempio l'inclusione di file locali per visualizzarne il contenuto (i file a rischio potrebbero essere /etc/passwd, i file di configurazione o quelli di log), l'alterazione del contenuto delle sessioni (che solitamente sono salvate all'interno della directory /tmp) e la meno comune ma non poco pericolosa alterazione dei file di upload temporanei. Questi problemi incorrono spesso a causa di una configurazione del server non corretta (come ad esempio il fatto di eseguire PHP come "nobody" sotto Apache, il che rende gli attacchi al filesystem problematici per tutti gli utenti del server) oppure a causa di controlli non meticolosi effettuati dagli sviluppatori sui dati utilizzati per l'accesso al filesystem.
Per comprendere se la nostra applicazione può essere soggetta ad attacchi di questo tipo è necessario individuare tutte le parti in cui l'input dell'utente è utilizzato come parte di nomi di file; se le variabili utilizzate per comporre il nome del file potrebbero non essere inizializzate e la PHP è stato configurato con la direttiva register_globals
ad on
, allora siamo a rischio.
Per proteggersi da questo tipo di attacchi gli sviluppatori dovrebbero assicurarsi che tutte le variabili utilizzate in questo contesto siano inizializzate prima del loro utilizzo, assicurarsi che gli utenti possano influenzare solamente le risorse accessibili per una determinata operazione, spostare tutto ciò che non è strettamente legato all'applicazione web al di fuori della root in modo che non sia accessibile dagli script PHP ed infine assicurarsi che gli script funzionino anche con la direttiva safe_mode attivata e correttamente configurata. Gli hoster, dal canto loro, dovrebbero installare PHP in un ambiente sicuro, spostare i file di configurazione e di sessione dalla loro locazione standard ad altre posizioni, abilitare le restrizioni relative alla direttiva open_basedir
ed eseguire PHP con i minor privilegi possibili.
Conclusioni
L'articolo, oltre a dare le informazioni da me riassunte ed integrate, fornisce anche dei riferimenti interessanti per approfondire gli argomenti e migliorare la sicurezza di PHP. In generale è bene ricordare che una libreria ben strutturata ed una configurazione di PHP ben studiata possono limitare al minimo i rischi di attacchi dannosi. Purtroppo spesso sono gli hoster che, lavorando in modo poco professionale, forniscono ambienti aperti a qualunque tipo di attacco, quindi è bene assicurarsi dell'ambiente in cui la nostra applicazione verrà eseguita e, in caso fosse un ambiente poco sicuro, cercare di irrobustire il più possibile la nostra applicazione in modo da non essere diretti responsabili di eventuali problemi.