Nella creazione di applicazioni scritte in PHP possono verificarsi principalmente due tipologie di errori:
- errori che determinano il malfunzionamento dello script o che ne impediscono del tutto l'esecuzione, si tratta in genere di errori sintattici;
- errori che non danno luogo necessariamente a malfunzionamenti dell'applicazione, ma possono esporre lo script a rischi legati alla scarsa sicurezza di determinate pratiche o rendere eccessivamente lunghe e macchinose le sessioni di sviluppo.
La prima tipologia è la più semplice da gestire, PHP stesso mette a disposizione un valido sistema per la notifica degli errori di sintassi con tanto di indicazione della riga di codice che ne è affetta, lo sviluppatore potrà così effettuare le correzioni del caso ponendo rimedio al malfunzionamento verificatosi.
La seconda tipologia è invece legata in genere a cattive abitudini di lavoro ed è quindi molto più insidiosa.
Queste due tipologie di errore in alcuni casi si incontrano e possono rivelarsi alquanto dannose se agiscono in combinazione; un esempio classico è quello relativo a due direttive ormai molto note del file di configurazione PHP.ini: REGISTER_GLOBALS
ed ERROR_REPORTING
.
Ancora oggi molti provider di hosting, soprattutto quelli che si ostinano a basare le proprie configurazioni su PHP 4, mantengono l'impostazione della REGISTER_GLOBALS
su On e impostano l'ERROR_REPORTING
su E_ALL & ~E_NOTICE in modo che gli errori notice non vengano visualizzati.
Inutile dare la colpa ai fornitori di spazio Web, la loro scelta attiene alla soddisfazione di esigenze che non riguardano questa trattazione; avvalendosi di una buona pratica di programmazione in cui, per esempio, si evita l'utilizzo di variabili non dichiarate, è possibile ovviare ai problemi derivanti dalle impostazioni errate delle due direttive appena citate.
Ed è di questo argomento che parleremo nei prossimi capitoli rivolgendoci in particolare all'utenza degli sviluppatori alle prime armi, cioè di come utilizzando alcuni accorgimenti sia possibile ovviare a problemi legati alla sicurezza e ridurre al minimo le lungaggini in sede di sviluppo. Cominciamo dal cosiddetto escaping dei parametri di input.
Il problema dell'escaping dei parametri di input
L'escaping è un argomento che riguarda in particolare gli input inviati dagli utenti, ogni volta che un client lancia una richiesta al nostro Web server determina un comportamento da parte della nostra applicazione: restituisce un risultato sotto forma di pagina HTML, consente l'accesso ad una pagina riservata, interroga un database, aggiunge, cancella o modifica un record; il più delle volte alcuni di questi comportamenti sono prodotti simultaneamente.
Nella maggior parte dei casi si tratta di comportamenti previsti e prevedibili; un'applicazione che presenta degli errori può dar vita ai comportamenti previsti nel momento in cui viene restituito l'output desiderato esattamente come un'applicazione ben scritta, ma a differenza di quest'ultima può dar vita anche a comportamenti imprevisti o imprevedibili.
Nell'Inglese tecnico esiste un verbo mutuato dal linguaggio comune, to sanitize, che spiega efficacemente l'importanza di filtrare i parametri di input derivanti dalle richieste degli utenti, una pratica che permette di utilizzare in tutta tranquillità i parametri sicuri rendendo inoffensivi quelli malevoli.
A priori non c'è dato di sapere quali siano le intenzioni di tutti i nostri utenti, controllare manualmente i parametri di input sarebbe irragionevole (diversamente, a cosa servirebbe PHP?) quindi è sempre meglio cautelarsi effettuando l'escaping di tutti gli input inviati tramite le nostre pagine.
Un esempio classico è quello relativo alle aree riservate per il cui accesso vengono richieste una userID e una password; raccogliere i parametri di input in questo modo:
$userid = $_GET['userid']; $password = $_GET['password'];
è una procedura sintatticamente corretta, ma non per questo sicura, infatti i dati inviati tramite il metodo GET non vengono filtrati, cioè purificati da componenti potenzialmente pericolose; non filtrare gli input potrebbe per esempio spianare la strada ad attacchi di tipo Cross-site Scripting (XSS) che sono abbastanza semplici da eseguire e consistono nel lanciare tramite browser codice malevolo, non di rado in Javascript, in modo da ottenere comportamenti non voluti dallo sviluppatore.
Osserviamo ora questo secondo sistema per la raccolta dei dati di input:
$userid = htmlspecialchars($_GET['userid'], ENT_QUOTES); $password = htmlspecialchars($_GET['password'], ENT_QUOTES);
htmlspecialchars()
è una funzione appositamente dedicata alla conversione di caratteri speciali in entità HTML: prendiamo il caso del codice Javascript in cui si utilizzano i marcatori <
e >
che corrispondono alle entità HTML <
e >
. Quindi, una volta operata la conversione dei marcatori essi non verrebbero più interpretati come dei delimitatori per il codice Javascript ma come semplici elementi di una stringa, in questo caso un tentativo di attacco tramite XSS sarebbe nella maggior parte dei casi inoffensivo.
L'argomento ENT_QUOTES
passato alla funzione htmlspecialchars()
consente di convertire in entità HTML qualsiasi tipo di apice per cui:
- o "" (doppio apice) viene convertito in
"
- o '' (singolo apice) diventa
'
In ogni caso PHP mette a disposizione anche un'altra funzione come htmlentities()
che converte tutti i possibili caratteri in entità HTML, o strip_tags()
che invece rimuove i tags HTML e PHP da una stringa ma integra strumenti per consentire la definizione di alcuni tags consentiti.
Con la pratica è possibile imparare ad utilizzare queste diverse funzioni sulla base delle diverse esigenze imposte dallo sviluppo di applicazioni differenti. Il fatto che PHP metta a disposizione funzioni per depurare le variabili da elementi potenzialmente pericolosi non significa che sia opportuno fare un uso disattento dei parametri di input; un esempio classico è quello relativo alle inclusioni:
<?php include('header.php'); include($_GET['nome_file']); include('footer.php'); ?>
Nel piccolo script appena mostrato abbiamo un'inclusione potenzialmente pericolosa, tramite GET potrebbe essere passato qualsiasi tipo di file, nello stesso tempo potrebbe essere molto semplice da parte di utenti malintenzionati sfruttare un costrutto del genere per recuperare informazioni importanti dal contenuto dei file presenti nel nostro spazio Web.
Le inclusioni di file tramite metodo sono sempre da sconsigliarsi.
Escaping degli input SQL
Un problema molto simile a quello trattato in precedenza riguarda il caso particolare della gestione degli input utilizzati per il lancio di istruzioni SQL verso i database; è molto importante rendersi conto del fatto che un database è in molti casi totalmente in balia dell'applicazione che si interfaccia ad esso: il DBMS richiede per la connessione un hostname, un nome utente e una password relativi alla base di dati che si desidera utilizzare, se questi dati sono riconosciuti come validi allora all'applicazione saranno accordati tutti i privilegi assegnati alla username utilizzata, compresi quelli che consento di agire in modo sostanziale sui dati.
Il problema in questo caso è quello molto noto delle SQL injections, esse non sono altro che degli exploit (codici che sfruttano vulnerabilità) in grado di sfruttare l'inefficienza dei controlli sui dati di input e di inviare codice maligno in una query.
Molti sviluppatori ritengono che per proteggersi dai pericoli derivanti dal mancato escaping manuale degli input SQL basti impostare in On le direttive Magic Quotes che dovrebbero occuparsi autonomamente e automaticamente dell'escaping; ma lo stesso manuale ufficiale di PHP sconsiglia questa pratica e consiglia invece di effettuare manualmente questa operazione tramite le apposite funzioni messe a disposizione dal linguaggio.
Inoltre, con PHP 6 questa funzionalità verrà rimossa in quanto deprecata (la stessa sorte che toccherà ad una direttiva altrettanto conosciuta come REGISTER_GLOBALS
), quindi è bene abituarsi sin da subito a non fare affidamento su di essa.
Le soluzioni alternative per filtrare gli input SQL lanciati verso le proprie basi di dati ci sono e sono più di una; una prima modalità potrebbe essere quella di utilizzare la funzione mysql_real_escape_string()
su ogni dato passato all'interno della query, come nell'esempio che segue:
$query = "UPDATE tabella SET campo='.mysql_real_escape_string($campo).' WHERE id='.mysql_real_escape_string($id).'"; @mysql_query($query) or die ('Impossibile eseguire la query' . mysql_error());
La funzione utilizzata ha il compito di aggiungere le sequenze di escape ai caratteri speciali contenuti in una stringa per l'utilizzo all'interno di un'istruzione SQL, tenendo conto del set di caratteri corrente usato per la connessione. Se, come sarebbe giusto, si decide di settare su Off Magic Quotes, è possibile utilizzare anche la funzione addslashes()
che restituisce in output una stringa con il carattere di backslah () posto prima dei caratteri che richiedono il quoting nelle query alle basi di dati come apici singoli, doppi apici, backslash e il byte NULL; usarla è molto semplice:
$nome = addslashes($_POST['nome']); $cognome = addslashes($_POST['cognome']); $citta = addslashes($_POST['citta']); $query = "INSERT INTO person (Nome, Cognome, Citta) VALUES ('$nome','$Cognome','$Citta')"; @mysql_query($query) or die ('Impossibile eseguire la query' . mysql_error());
In questo modo una stringa come "L'aquila" verrà filtrata e inserita in tabella sotto questa forma: "L'Aquila"; per produrre un output accettabile sarà quindi necessario rifiltrare la stringa in modo da eliminare il carattere di backslah aggiunto, per far questo sarà sufficiente passare la variabile che la contiene alla funzione stripslashes()
(che ha il compito preciso di rimuove gli slash aggiunti con addslashes()
):
$citta = stripslashes($row['citta']);
Si tenga conto che addslashes
in alcuni casi può non essere sufficiente a soddisfare le esigenze di sicurezza richieste per determinate procedure, è quindi sempre bene tenere presente che PHP fornisce anche altri costrutti per l'escaping come quelli indicati nel capitolo precedente.
Conclusioni
L'argomento relativo agli errori che si possono compiere durante le sessioni di sviluppo in PHP è molto vasto, in questa breve trattazione ci siamo limitati ad analizzare gli errori derivanti da cattive abitudini di lavoro, tralasciando quelli derivanti da un utilizzo erroneo della sintassi che possono essere più facilmente gestiti. Particolare attenzione è stata posta nei riguardi di quegli errori che possono mettere a rischio le applicazioni e dar vita a vulnerabilità che potrebbero essere sfruttate da utenti malintenzionati per pregiudicare il funzionamento dei nostri script.