Lezione Precedente Elenco Lezioni Lezione Successiva

Laboratorio di Sistemi Informativi

Sicurezza nelle applicazioni web

Finora abbiamo quasi ignorato uno dei problemi fondamentali nello sviluppo di applicazioni web: la sicurezza. Il fatto di coinvolgere componenti diverse (server web, server SQL, script in PHP, etc..) rende particolarmente vulnerabili questo tipo di software. Ma da cosa nascono i problemi di sicurezza? Il fulcro di tutto è il fatto che le applicazioni hanno bisogno di accettare input provenienti dall'esterno, ovvero dagli utenti. Il programmatore ha certe aspettative su che cosa l'utente fornirà in input e non si preoccupa (o comunque non si accorge) di cosa potrebbe accadere qualora queste aspettative venissero violate. In questo modo un hacker, fornendo un input non previsto, può alterare il funzionamento normale di una applicazione.

Questa descrizione, attualmente un po' astratta, sarà più comprensibile dopo che avremo visto alcune delle forme di attacco più comuni che vengono portate alle applicazioni web. Vedremo anche alcuni degli accorgimenti da prendere in modo da scrivere codice PHP il più robusto possibile.

Prima di andare avanti, tuttavia, è bene precisare che la sicurezza non è una caratteristica che c'è o non c'è: le applicazioni (e i sistemi informatici in generale) possono essere più o meno sicuri. Aumentare la sicurezza dell'applicazione ha dei costi, sia nella fase di sviluppo della stessa, che in quella di utilizzo. Tipicamente, infatti, all'aumentare della sicurezza di un sistema, il suo utilizzo diventa sempre più macchinoso. In questa lezione vedremo alcune misure di sicurezza di base che ci si aspetta vengano applicate in tutti le applicazioni web.

Spoofed Form Submission

Quando abbiamo parlato di validazione dei dati abbiamo detto che è importante validare i dati inviati dall'utente a livello di server, perché i conrolli a livello di client possono essere aggirati. Naturalmente JavaScript può essere disabilitato, ma anche controlli che apparirebbero più sicuri, come la lunghezza dei campi di input o le possibili scelte nei campi di tipo SELECT sono tranquillamente eludibili. Il punto è che l'utente di una applicazione web non è obbligato ad accedere alla nostra applicazione con le form che progettiamo noi. Si consideri ad esempio la pagina colori-form.html:

<form action="colori-process.php" method="POST">
<select name="color">
    <option value="red">red</option>
    <option value="green">green</option>
    <option value="blue">blue</option>
</select>
<input type="submit" />
</form>

e il seguente file colori-process.php:

<?php
echo $_POST['color'];
?>

Normalmente l'utente della applicazione passerà dalla form colori-form.html per accedere a colori-process.php, ma ciò non è necessario. Invece, l'utente si può creare una propria form, come più desidera, sul proprio computer locale, a patto di usare una URL assoluta nell'attributo action del tag form, come per esempio in

<form action="http://<server>/<path>/colori-process.php" method="POST">
<input type="text" name="color" />
<input type="submit" />
</form>

Adesso, riempiendo opportunamente la form modificata, l'utente può fornire qualunque input desideri al file colori-process.php.

Come ulteriore esempio, si consideri la procedura di inserimento in inserimento-validate2.php. Si potrebbe pensare che, dato l'uso di campi a scelta multiplica per la selezione della categoria, sia inutile validare questi dati prima dell'inserimento. Ma, come abbiamo visto prima, si tratta solo di una illusione. Un cracker può tranquillamente modificare la form originale trasformando i campi a scelta multipla in campi di input testuali ed aggirare i nostri controlli. Occorre quindi controllare lato server anche questi campi, ottenendo inserimento-validate3.php.

Un'altra cosa su cui non si può fare affidamente è la lunghezza degli input immessi dall'utente. In teoria esiste l'attributo maxlength nei campi di input che limita la lunghezza massima delle stringhe che l'utente può immettere ma, con lo stesso stratagemma visto sopra, è facile cambiare le form ed eliminare queste restrizioni.

Si noti anche che esistono degli strumenti per sviluppatori web che consentono, senza fare fatica alcuna, di trasformare un campo a risposta multipla in un campo di testo, eliminare le lunghezze massime dei campi di input, e in generare alterare in vari modi le pagine web generate dalle applicazioni. In mani esperte, questi software si trasformano in utili strumento di hackeraggio. Uno di questi software è, ad esempio, l'estensione Web Developer per Firefox.

Spoofed HTTP request (materiale aggiuntivo)

La creazione di una form speciale, come fatto nella sezione precedente, è addirittura superflua quando il metodo usato per il passaggio dei parametri è GET anziché POST. In quest'ultimo caso basta inserire i parametri opportuni nella URL. Anche nel caso del metodo POST, tuttavia, creare una form speciale è solo una questione di convenienza, ma si potrebbero lo stesso passare i parametri opportuni allo script desiderato aprendo una connessione con il server web usando, invece del browser, un programma generico per connessioni TCP come il programma telnet. Ad esempio, per invocare colori-process.php col parametro color impostato a pink basta dare il comando:

telnet <server> http

e fornire una opportuna richiesta HTTP, come quella che segue:

POST <percorso>/colori-process.php HTTP/1.1
Host: <server>
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20060601 Firefox/2.0.0.3 (Ubuntu-edgy)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.7,it;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 10

color=pink

A questo punto il server risponderà con la seguente:

HTTP/1.1 200 OK
Date: Thu, 03 May 2007 10:36:13 GMT
Server: Apache/2.0.55 (Ubuntu) DAV/2 SVN/1.3.2 PHP/5.1.6
X-Powered-By: PHP/5.1.6
Content-Length: 4
Connection: close
Content-Type: text/html

pink

che verrà visualizzata dal programma telnet direttamente sullo schermo.

Anche se non conoscete tutti i dettagli del protocollo HTTP, dovrebbe essere chiaro che, collegandosi in questo modo al server web, è possibile inviare tutta una serie di richieste che non è possibile effettuare normalente tramite un browser. Dunque, quando progettate una applicazione, pensate sempre al fatto che un eventuale cracker non solo potrebbe non utilizzare le pagine web che gli preparate voi, ma potrebbe anche non utilizzare un browser, ma mandare comandi manualmente con telnet o con un client da lui scritto appositamente.

Local proxy

Al posto del comando telnet è possibile utilizzare del software specifico che consente di esaminare e manipolare una richiesta HTTP prima di mandarla al server: un local proxy. Il local proxy è un software che filtra tutto ciò che entre ed esce dal browser. Normalmente se ne sta buono, semplicemente analizzando il flusso di dati e consentendo all'utente di esaminarlo, esattamente come l'add-on LiveHttpHeaders che abbiamo usato più volte. Quando si vuole, però, si può utilizzare il local proxy per modificare le richieste HTTP, prima di inviarle al server.

Uno dei local proxy più diffusi è WebScarab. Si tratta di una applicazione java, che potete scaricare dal sito del produttore o cliccando qui. L'applicazione attiva un proxy server su localhost alla porta 8008. Basta inserire questi dati nelle opzioni di configurazione di rete del browser (su Firefox vi si accede con Edit -> Preferences -> Advanced -> Network -> Settings) e il proxy entra in funzione. Ecco come si presenta la finestra di configurazione di Firefox per abilitare l'uso di WebScarab:

screenshot delle opzioni di rete di Firefox

mentre questo è un esempio di output che si ottiene accedendo a colori-form.html, scegliendo il colore red e cliccando il pulsante di sottomissione.

screenshot di webscarab

Dalla sezione Intercept è possibile intercettare la richiesta ad colori-process.php e rimpiazzare il colore red con pink.

webscarab in modalità intercept

SQL Injection

Abbiamo già visto come l'uso di comandi SQL contenenti parametri provenienti dall'esterno può essere pericoloso a causa del problema noto come SQL Injection. Abbiamo anche visto come evitare questo tipo di attacco con l'uso dei prepared statement. Facciamo adesso solo un ulteriore esempio dei guai che possono accadere altrimenti. Consideriamo lo script login3-bug.php, del tutto equivalente a login3.php, tranne per il fatto che non utilizza i prepared statement e costruisce manualmente la stringa con la query da inviare al database. In pratica, si rimpiazza

$stmt = $conn->prepare("SELECT * FROM utenti WHERE username=? and password=? and admin=true");
$stmt->execute(array($_POST['user'],sha1($_POST['passwd'])));
if ($stmt->fetch()) {

con

$hashedpwd = sha1($_POST['passwd']);
$stmt = $conn->query("SELECT * FROM utenti WHERE username='$_POST[user]' and password='$hashedpwd' and admin=true");
if ($stmt->fetch()) {

Il problema di questo script è che se immetto nel campo username una stringa contenente apici, questi vengono inseriti tali e quali nella query che segue

SELECT * FROM utenti WHERE username='$_POST[user]' AND passwd='$hashedpwd' AND admin=true

al posto di $_POST[user]. Ciò potrebbe dare origine a vari problemi: ad esempio, lo script potrebbe fallire, restituendo degli errori all'utente, nel caso la query SQL risultasse mal formata. Ma peggio ancora, è possibile autenticarsi con successo senza sapere nè la password nè la username!!!!!!

Supponiamo per il momento di sapere che uno degli utenti privilegiati ha username paolo, ma di non conoscerne la password. Posso comunque ottenere l'accesso alla sezione riservata specificando una password a caso, ma con il segunte valore di login: paolo' or ''='. Così facendo,  la query che viene passata a MySQL è la seguente:

SELECT * FROM utenti WHERE username=paolo' or ''='' AND passwd='<hashed password>' AND admin=true

che restituisce tutte le righe della tabella utenti con username pari ad paolo, in quanto il controllo della password è sempre in "or" con la condizione username='paolo'. In questo modo, il controllo successivo ha successo e ciò consente l'accesso alle pagine web riservate.


Esperimento. Un valore appena più complesso per il campo username consente di avere accesso all'area riservata senza conoscere neanche una login valida. Vi viene in mente come?
Come abbiamo detto, un metodo standard per proteggersi da questo tipo di attacco è l'uso dei prepared statements, ogni qual volta il comando SQL da utilizzare contenga dei parametri provenienti dall'esterno.

Cross-Site Scripting (e simili)

Un attacco di tipo SQL Injection si ottiene sfruttando il fatto che un dato può contenere dei caratteri che vengono interpretati in maniera "semanticamente errata" all'interno di una query SQL. Un tipo di attacco simile è il Cross-Site Scripting (XSS) che, però, usa HTML come "veicolo": cosa succede se dei dati che vengono visalizzati sullo schermo contengono dei tag HTML? Ovviamente, i tag vengono interpretati dal browser.

Ad esempio, se inseriamo un oggetto con descrizione breve <b>prova</b>, la parola prova verrà visualizzata in grassetto nell'elenco degli oggetti. Il problema non è tanto che è possibile inserire una voce in grassetto (che, in questo caso specifico, potrebbe anche essere positivo), ma che è possibile inserire del codice in JavaScript, che può essere eseguito dal browser dell'ignaro utente che si connette al sito web. Ad esempio, cosa succede se si inserisce come descrizione di un oggetto la stringa prova<script> alert('XSS');</script> ?

Nel caso meno grave, una debolezza di questo tipo consente di rendere inservibile il nostro sito web (cosa che in gergo informatico prende il nome di attacco DOS, acronimo di Denial Of Service). Ma nei casi più gravi, il codice JavaScript può fare cose anche più complesse, ad esempio redirigere il browser dell'utente a un sito web che sembra uguale a quello originario, ma non lo è ed è invece gestito da un cracker. A questo punto l'utente ignaro potrebbe effettuare un login, pensando di essere nel sito Mancolista, fornendo così la password all'cracker.

Il modo più semplice per risolvere il problema, quando si vuole precludere del tutto la possibilità di usare tag HTML, è quotare le stringhe provenienti dall'esterno o dal database prima di mandarle in output sulla pagina web. A tale scopo si puà usare la funzione:

Se prima di mandare una stringa in output la si processa con htmlspecialchars, siamo sicuri che eventuali tag HTML non verranno interpretati come tali. Nel nostro caso, si tratta di modificare la riga
echo "<a href='dettagli.php?id={$riga['id']}'>",$riga["nome"],"</a> : ",$riga["descrizione"];
in elenco3.php con
echo "<a href='dettagli.php?id={$riga['id']}'>",htmlspecialchars($riga["nome"]),"</a> : ",htmlspecialchars($riga["descrizione"]);

ATTENZIONE 1. Ovviamente, non basta modificare solo il file elenco3.php: la funzione htmlspecialchars va utilizzata in qualunque punto del programma vengono mandati in output dati proveniente, in un modo o nell'altro, da una fonte esterna.
ATTENZIONE 2. Notare che $riga['id'] non viene protetto con htmlspecialchars. Questo perché il campo id del database è un campo numerico. Visto che la variabile $riga viene estratta dal database, siamo sicuri che $riga['id'] è un valore numerico e quindi non contiene caratteri interpretabili in maniera particolare dal browser. È vero che c'è un minimo rischio che un hacker riesca a modificare il tipo del campo id e a inserire al suo interno valori non numerici, quindi potremmo pensare di usare htmlspecialchars anche per $riga['id']. Ci sembra però che se il nostro hacker è riuscito a fare tutto questo, ha probabilmente trovato strade molto più efficaci per violare i nostri sistemi di sicurezza.

Questa soluzione non va più bene qualora si vogliano interpretare correttamente alcuni tag HTML (ad esempio quelli per il grassetto e corsivo) ma ignorarne altri. In questo caso bisogna prevedere delle procedure di filtraggio più complesse, che non abbiamo però tempo di approfondire in questo corso.

Notare anche che non sempre è facile capire esattamente cosa provenga dall'esterno. Ovviamente ciò è vero per tutto ciò che si trova all'interno di $_GET e $_POST, così come per i dati presenti nel database, ma anche molte delle componenti di $_SERVER possono essere modificate dall'esterno e costituire un problema. Ad esempio, guardate cosa succede in login3.php, quando si accede alla pagina tramite la seguente URL:

http://<server>/<percorso>/login3.php/" method="POST"><script>alert("XSS");</script><fake x="

Il problema è sempre lo stesso, questa volta originato dalla variabile $_SERVER['PHP_SELF']. Un cracker potrebbe creare una pagina web con un link a login3.php come quello di sopra. Tutti i navigatori incauti che si trovano a cliccare su quel link, si ritrovano nella pagina di login del siro Mancolista, ma con un più un pezzo di JavaScript in esecuzione che potrebbe modificare, a loro insaputa, il comportamento dell'applicazione (ad esempio potrebbe controllare la password inserita e inviarla su Internet). Non sempre queste debolezze si riescono a sfruttare veramente, ma è meglio essere sicuri e quotare tutto quello che è necessario. Nel caso della procedura login si tratta di rimpiazzare la riga:

<form action="<?php echo $_SERVER['PHP_SELF']?>" method="POST"> 
con
<form action="<?php echo htmlspecialchars($_SERVER['PHP_SELF'])?>" method="POST"> 
o anche con
<form action="" method="POST"> 
dove abbiamo eliminato il problema alla radice usando la URL vuota che, come già detto altre volte a lezioni, può essere usata per puntare alla pagina corrente.

Parametri passati tramite URL

Un'altra fonte di possibili malfunzionamenti del programma web è la codifica delle URL nei link HTML e nelle funzioni che interagiscono direttamente con il protocollo HTTP, come ad esempio redirect_browser(). Il problema si verifica quando passiamo dei parametri tramite la URL, e tali parametri sono di tipo alfanumerico. Nella nostra applicazione Mancolista, gli unici parametri che passiamo manualmente (cioè senza l'uso di una form, per la quale non ci sono problemi) sulla URL sono valori numerici, per cui non ci sono problemi.

Per poter fare un esempio, supponiamo che la tabella oggetti non abbia il campo id, e che il campo nome sia la chiave primaria. Di conseguenza, bisogna modificare tutte le pagine che operano sulla tabella oggetti, in particolare elenco3.php e dettagli.php, ottenendo elenco3-nomeid.php e dettagli-nomeid.php. Consideriamo la linea in elenco3-nomeid.php che genera il link da premere per andare alla pagina dei dettagli:

echo "<a href='dettagli-nomeid.php?id=".htmlspecialchars($riga['nome'])."'>",
     htmlspecialchars($riga["nome"]),"</a> : ",htmlspecialchars($riga["descrizione"]);

Notare l'uso di htmlspecialchars per impedire che caratteri speciali presenti nel nome possano erroneamente essere interpretati come tag HTML. Purtroppo, questo programma non funziona bene. Supponiamo esista un oggetto con un nome contenente la &, ad esempio a&b. La riga di cui sopra genera il link:

<a href='dettagli-nomeid.php?id=a&amp;b'>"a&amp;b</a>

che, per le regole di codifica di HTML, causa sullo schermo l'apparizione della scritta a&b che, quando premuta, fa saltare all'indirizzo dettagli-nomeid.php?id=a&b. Il problema è però che il parametro id=a&b viene intepretato da PHP come due parametri diversi: id di valore a e b con un valore non specificato. Questo perché la & nei link separa parametri diversi. Quello che bisogna fare è allora, prima ancora di chiamare htmlspecialchars, trasformare (quotare) il nome in modo che non contenga caratteri che hanno significati speciali nelle URL. Questo è possibile perché qualunque carattere può essere rappresentato in una URL rimpiazzandolo con la stringa %xx dove xx è il codice ASCII esadecimale del carattere. Ad esempio, la & può essere rappresentata come %26, perdendo in questo modo il suo significato di separatore di parametri. A fare questa trasformazione pensa la funzione rawurlencode:

Il comando echo di cui sopra dovrebbe allora diventare:

echo "<a href='dettagli-nomeid.php?id=".htmlspecialchars(rawurlencode($riga['nome']))."'>",
     htmlspecialchars($riga["nome"]),"</a> : ",htmlspecialchars($riga["descrizione"]);

In realtà, siccome rawurlencode genera delle stringhe che non hanno nessuno dei caratteri speciali di HTML, l'ulteriore passaggio con htmlspecialchars è inutile, e la riga di sopra si può semplificare in:

echo "<a href='dettagli-nomeid.php?id=".rawurlencode($riga['nome'])."'>",
     htmlspecialchars($riga["nome"]),"</a> : ",htmlspecialchars($riga["descrizione"]);

Notare che lo stesso problema si presenta quando abbiamo dei parametri passati nella URL utilizzando la funzione header (o redirect_browser). In questo caso, se il parametro può contenere potenzialmente anche caratteri che sono interpretati in maniera speciali nelle URL, occorre quotarlo con rawurlencode. Ad esempio, un comando come:

redirect_browser("$_SERVER[PHP_SELF]?id=$_POST[id]");

andrebbe rimpiazzato con

redirect_browser("$_SERVER[PHP_SELF]?id=".rawurlencode($_POST['id']));

visto che potenzialmente $_POST['id'], proveniendo dall'esterno, può contenere caratteri alfabetici.

A prima vista questo delle URL sembra più una possibile fonte di malfunzionamenti del programma che un vero e proprio problema di sicurezza. Tuttavia, non è da escludere che qualche hacker dotato di un po' più di inventiva riesca a sfruttare in qualche modo queste debolezze. Meglio essere sicuri e proteggersi anche da questa evenienza.

Andata a capo e tag <br>

Una problematica simile a quanto visto sopra, non legata a motivi di sicurezza ma al buon funzionamento del programma, è il trattamento del carattere di andata a capo. Nella nostra applicazione abbiamo il campo descrizione_lunga che è pensato per contenere descrizioni dettagliate degli oggetti, possibilmente composte da vari paragrafi con andate a capo tra di essi. Allo stato attuale, però, questo non funziona.

Anche se nel campo dedicato alla descrizione lunga inserisco un dato del tipo:

riga 1
riga 2

quando questo viene visualizzato nella pagina dei dettagli, le due righe diventano una riga sola:

riga 1 riga 2

Questo avviene perché il carattere di andata a capo (codice ascii 16), che inserisco nel campo di input, viene correttamente inserito nel pagina HTML creata dallo script dettagli.php, ma viene del tutto ignorato dal browser, visto che in HTML per andare a capo occorre inseriro un tag <br> esplicito.

Siamo di fronte ad un caso opposto a quello dell'attaco di cross-site scripting: qui vogliamo trasformare alcuni caratteri (l'andata a capo) in un tag HTML in modo che venga interpretato in maniera speciale dal browser. Per fortuna c'è una funzione PHP creata apposta per questo:

Possiamo applicare tutto quanto visto fin'ora alla pagina dei dettagli per ottenere dettagli-nl2br.php, ottenuto dalla vecchia pagina dettagli.php rimpiazzando

echo "<strong>${riga["nome"]}</strong><br>";
echo $riga["descrizione_lunga"];

con

echo "<strong>",htmlspecialchars($riga["nome"]),"</strong><br>";
echo nl2br(htmlspecialchars($riga["descrizione_lunga"]));

Altri problemi di sicurezza per le applicazioni PHP

Esposizione delle credenziali di accesso

Quando un'applicazione web deve collegarsi ad un DBMS è di solito necessario fornire delle credenziali, tipicamente nella forma di una username e una password. Nel nostro caso, username e password sono sempre stati memorizzati nel file config.php, che si trova nella stessa directory dell'applicazione vera e propria. La soluzione non è delle più sicure, perché una eventuale errata configurazione del server web potrebbe rendere visibile questo file come file di testo dal browser dell'utente (basterebbe che il sistemista disabiliti temporaneamente il supporto PHP). Comunque, è sempre meglio che chiamare il file con una estensione diversa da php, che invece renderebbe quasi certa la possibilità di accesso alla credenziali per la connessione. Se ad esempio chiamassimo il file config.inc invece di config.php, su quasi tutti i sistemi questo sarebbe tranquillamente visibile dal browser utente.

La soluzione ottimale è probabilmente quella di mettere il file in una directory che non è raggiungibile dal server web, ad esempio direttamente nella propria home invece che in public_html. Basterebbe poi modificare il comando require 'config.php' in maniera opportuna (ad esempio in require '../config.php'). In questo modo, indipendentemente dal nome del file e dallo stato di funzionamento del server web, le credenziali di accesso al DBMS sarebbero inaccessibili ai browser. Tuttavia, la soluzione è sicuramente un po' più scomoda, perché ci costringe a tenere dei file che logicamente starebbero assieme collocati in posizione diverse dell'albero delle directory. Per questo motivo, per i nostri esperimenti ci accontenteremo di un livello di sicurezza inferiore e terremo il file config.php assieme a tutti gli altri.

Buffer overflow

Talvolta il nostro programma è scritto in modo da aspettarsi in input stringhe di una certa lunghezza massima. Cosa succede se gli arriva una stringa più grande? Sui linguaggi evoluti come PHP  o Java, questo non è un problema particolarmente grave, ma quando i programmi sono scritti in linguaggi come il C, la cosa si fa seria. In C, quando si usa una stringa, bisogna dichiararne la lunghezza. Se poi si prova a metterci dentro una stringa più lunga di quella massima, i dati "sforano" e sovrascrivono la memoria al di fuori dello spazio riservato... a questo punto tutto può succedere. Questo, effettivamente, è uno degli attacchi più comuni, che prende il nome di buffer overflow

In PHP e Java la memoria viene gestita dal linguaggio e non direttamente dal programmatore, per cui non ci dovrebbero essere problemi. Però, c'è sempre il rischio di un bug nell'interprete PHP, Apache o MySQL, per cui è meglio, prima di usare delle stringhe prese in input, troncarle ad una dimensione accettabile (un famoso esempio di vulnerabilità a buffer overflow in PHP è stato da imputare alla funzione wordwrap). Ad esempio, se la variabile $_POST['nome']  deve essere inserita in un campo del database lungo 255 caratteri, come effettivamente è, possiamo tranquillamente troncarla a questa dimensione.

In alternativa, possiamo troncare tutte le stringhe in input alla stessa lunghezza, scegliendo un valore abbastanza grande da non creare problemi con l'applicazione, ma abbastanza piccolo da renderci sufficientemente sicuri che eventuali vulnerabilità di PHP, Apache o MySQL non possano venire sfruttate adeguatamente (ad esempio, per la nostra applicazione, un valore intorno al migliaio dovrebbe andare bene).

Per troncare le strighe, possiamo usare la funzione predefina substr:

Per fare un esempio, modifichiamo lo script login3.php troncando la username e la password a 255 caratteri, prima di passarle alle funzioni PDOStatemente::execute e sha1. In questo modo, eventuali vulnerabilità a buffer overflow di queste due funzioni non potranno essere sfruttate da un hacker. Si tratta in pratica di rimpiazzare la riga:

$stmt->execute(array($_POST['user'],sha1($_POST['passwd'])));
con
$stmt->execute(array(substr($_POST['user'],0,255),sha1(substr($_POST['passwd'],0,255)));

Per chi fosse interessato, una definizione più estesa del concetto di buffer overflow si può trovare in questo articolo di searchSecurity.com, mentre una descrizione molto più tecnica si trova su questo articolo di LinuxJournal.

Uno schema generale per applicazioni sicure

Sembra ormai chiaro che i dati che vengono presi in input dall'ambiente esterno costituiscono una fonte di guai senza limite. È bene quindi avere una strategia per gestirli nel migliore dei modi. I punti fondamentali sono due:

Utilizzando questi principi possiamo riscrivere tutte le pagine relative alla nostra applicazione: otteniamo le pagine in mancolista-v4.zip. In queste pagine, tutti gli input sono troncati alla lunghezza prevista dal database. Nel caso del campo password, non essendo sensato usare 40 che è la lunghezza della firma della password, abbiamo utilizzato 255. Inoltre, i campi che si suppone siano di tipo intero sono stati convertiti forzatamente con la funzione intval. Si noti inoltre l'uso della @ in inserimento-validate4.php sui campi $_POST['descrizione'], $_POST['descrizione_lunga'], etc... nella sezione di inserimento. La @ serve a evitare che si generino messaggi di errore, nel caso lo script sia chiamato manualmente fornendo come input il campo nome ma non gli altri campi.

Si è deciso di utilizzare un approccio permissivo al problema della validazione dati. Un approccio alternativo sarebbe stato quello di controllare che i valori in input supposti interi fossero effettivamente tali, invece di trasformali in interi, e nel caso ciò fosse falso bloccare il corrispondente script, magari memorizzando informazioni su quanto accaduto da qualche parte (un altra tabella del database?) per consentire agli amministratori di sistema se si trattava di un errore saltuario o di un tentativo di intrusione in atto. Analogamente, avvertimento simile potrebbe essere generato nel caso $_POST['nome'] fosse settato, ma non gli altri campi di input.


Nota. Notare che, anche in quest'ultima versione di Mancolista, ci sono alcune cose che potremmo modificare per migliorare la sicurezza. Ad esempio, abbiamo supposto che le variabili che si trovano dentro l'array $_SESSION siano sicure (in quanto le abbiamo settate noi) e quindi abbiamo evitato di usare la funzione substr su di esse. Ma siamo veramente sicuri che un cracker non riesca a intrufolarsi nel nostro server web e, in qualche modo, alterare le variabili di sessione (che, ricordiamo, sono dei file che risiedono sul server?). E se ci fosse un bug di tipo buffer-overflow nella funzione htmlspecialchars di PHP, il cracker non potrebbe tentare di sfruttare la nostra ingenua dimenticanza per danneggiare la nostra applicazione? Ovviamente tutto ciò si potrebbe considerare paranoico, ma è importante per far capire che non esiste limite al grado di sicurezza che si può richiedere ad una applicazione. Nel proseguimento del corso, quello che abbiamo fatto per la versione 4 di Mancolista sarà più che suficiente. Anzi, in certe occasioni saremo anche meno pignoli.


Conclusioni

Quelli che abbiamo visto sono solo alcuni dei problemi di sicurezza che si possono presentare. Tuttavia, il problema generale della sicurezza dei sistemi informativi in rete è ben più vasto, e sicuramente andrebbe affrontato in un corso interamente dedicato.

Ad ogni modo, una breve guida sulla sicurezza delle applicazioni PHP è la PHP Security Guide. Qui vogliamo invece elencare altri aspetti tipici delle problematiche di sicurezza, non strettamente collegati al PHP:

Per finire, alcuni consigli su come aumentare la sicurezza dei sistemi di database:

Post-scriptum: hackers e crackers

Vorrei concludere con una nota finale sulla parola hacker. I media tendono ad utilizzare il termine come sinonimo di "criminale informatico". Questo però non è il vero significato della parola. Wikipedia definisce hacker come una "persona che si impegna nell'affrontare sfide intellettuali per aggirare o superare creativamente le limitazioni che gli vengono imposte, non limitatamente ai suoi ambiti d'interesse (che di solito comprendono l'informatica o l'ingegneria elettronica), ma in tutti gli aspetti della sua vita". Rimanendo in ambito informatico, gli hacker più bravi conoscono così a fondo gli strumenti hardware e software che utilizzano che sono in grado di fare cose impossibili ad altri, e spesso non previste neanche dai progettisti di questi sistemi. Questo ovviamente include anche cose che potrebbero essere illegali (a seconda della giurisdizione locale), come violare sistemi di protezione, purché lo scopo non sia provocare danno o trarre profitto, ma la sfida intellettuale che c'è sotto. Anche Bill Gates in passato è stato un hacker.

Il termine che più correttamente corrisponde a criminale informatico è invece cracker. Ovviamente tra i due termini esistono notevoli relazioni: un cracker è spesso (ma non sempre) anche un hacker, che però decide di sfruttare le sue capacità per trarre profitto personale o per fare atti di vandalismo.

Lezione Precedente Elenco Lezioni Lezione Successiva

Valid HTML 4.01 Transitional Valid CSS!