Alcuni anni fa realizzare un sito web significava produrre immagini, scrivere del buon codice HTML e assemblare il tutto in modo armonico. Tutto era statico, per modificare una pagina era necessario cambiare manualmente il codice HTML e caricarla di nuovo sul server. L'avvento degli application server ha permesso di integrare il Web con i database e produrre prima siti dinamici, ovvero in grado di autoaggiornarsi estraendo dati da un database, poi vere e proprie applicazioni, che in alcuni casi hanno soppiantato i programmi tradizionali. Per vari motivi, che vedremo in seguito, la filosofia costruttiva di una Web application differisce notevolmente dalla programmazione tradizionale, cosė come i criteri di protezione che devono essere utilizzati.
Il concetto è molto semplice: si tratta di fare in modo che il Web application server esegua sul database di back end codice SQL arbitrario "iniettato" da un utente non privilegiato (un normale utente del sito Web per intenderci) nella Web application.
Anzitutto è bene chiarire che per ottenere ciò è abbastanza raro che si possano sfruttare bug o problemi intrinsechi dell'application server, molto più spesso è il fattore umano, ovvero la negligenza di chi ha progettato o realizzato la Web application, a offrire le migliori opportunità.
Gli scopi possono essere molteplici, dalla sottrazione illecita di dati alla manipolazione degli stessi, fino alla e propria azione di distruzione delle informazioni.
Un esempio: fingiamo di iscriverci al sito di pura fantasia www.webapp-debole.com. Al termine della procedura d'iscrizione effettuiamo il login e veniamo riconosciuti dal sito che, immediatamente, si adegua e ci chiama per nome. C'è anche un piccolo menu per gli utenti registrati, che consente tra le altre cose di visualizzare e aggiornare i propri dati. Clicchiamo per visualizzare i nostri dati e veniamo trasferiti a una pagina che ci mostra chi siamo, l'indirizzo, i recapiti e tutto il resto. Notiamo che l'URL della pagina è http://www.webapp-debole.com/showuser.asp?id=3264: se ne deduce con facilità che la query SQL generata da questa pagina è qualcosa del tipo SELECT *FROM Utenti WHERE id = 3264.
Modifichiamo quindi manualmente l'id nell'URL del nostro browser nel modo seguente http://www.webapp-debole.com/showuser.asp?id=3263.
In assenza di ulteriori controlli, la Web application eseguirà la seguente query SELECT *FROM Utenti WHERE id = 3263 e ci mostrerà tutti i dati dell'utente che si è iscritto prima di noi. Ora sappiamo che questa Web application è debole per vari motivi: perché non effettua un controllo di coincidenza tra l'utente autenticato e i dati dell'utente richiesto e perché utilizzando id numerici sequenziali è troppo facile indovinare gli identificativi degli altri utenti.
A questo punto, se volessimo rubare l'intero database utenti del sito in questione sarebbe sufficiente iniettare una condizione booleana sempre vera nella query. Trasformiamo il nostro URL in questo modo http://www.webapp-debole.com/showuser.asp?id=3263%20OR%201=1: cosė facendo forziamo la Web application a eseguire la seguente query SELECT *FROM Utenti WHERE id = 3263 OR 1=1.
L'operazione di OR tra una qualsiasi condizione e una tautologia (espressione sempre vera) è a sua volta in una condizione che è sempre vera per ogni record contenuto nel database. Questa query porta quindi all'estrazione di tutti i dati di tutti gli utenti del database del sito. Da notare che questo tipo di attacco avrebbe funzionato anche se la tipologia di id scelta fosse stata più sicura rispetto a quella dei numeri sequenziali.
I progettisti della nostra Web application immaginaria, avvertiti del problema, decidono di spostare il campo id dall'URL all'interno del codice HTML delle pagine cambiando da get a post il metodo per le richieste HTTP. Questo si rivela però un falso rimedio, in quanto è sempre possibile salvare la pagina, modificarla offline ed eseguire il post partendo dalla pagina modificata sul nostro computer. Questo è ancor più vero quando si tratta, per esempio, di e-commerce.
Poniamo caso di voler acquistare un prodotto del valore di 750 euro, ma di volerlo pagare solamente 7,5 euro. Al termine della procedura di acquisto, il sito produrrà una pagina con diversi campi hidden contenenti i dati necessari per la transazione da passare al Web server della banca che deve effettuare l'addebito sulla carta di credito. È un passaggio delicato, in quanto per alcuni secondi l'utente abbandona il sito del merchant e passa su quello della banca. Il sito della banca non sa a priori se i dati che gli arrivano sono stati in qualche modo modificati.
Identifichiamo dunque l'ultima pagina del sito del merchant, quella che passa i dati alla Web application bancaria e nel codice HTML rileviamo i seguenti campi
<input type="hidden" name="OrderID" value="36527">
<input type="hidden" name="Description" value="Ordine Prodotto X">
<input type="hidden" name="Amount" value="750.00">.
Salviamo la pagina sul nostro hard disk e velocemente (prima che scada la sessione) modifichiamo il terzo campo hidden facendolo diventare <input type="hidden" name="Amount" value="7.50">.
Carichiamo la pagina dal nostro hard disk nel browser e "postiamo" i dati al Web server della banca. Come supponevamo, non viene effettuato un controllo incrociato e quindi il nostro pagamento è approvato (per un totale di 7,50 euro). Il successivo ritorno al sito di origine causerà la conferma dell'ordine numero 36527, in quanto la banca lo segnala come pagato correttamente.
Vi sarà capitato di visitare un sito che consente di effettuare ricerche nel database dei prodotti in vendita. Se questi sono molti, è comodo avere una visualizzazione suddivisa in più pagine, diciamo 10 prodotti per pagina. Un tipo di esposizione dei risultati molto comodo, mutuato dai motori di ricerca. Ci possiamo muovere tra una pagina e l'altra utilizzando dei pratici bottoni "precedente" e "successiva" posti in calce alla tabella dei risultati parziali. Possiamo immaginare che per presentare la sequenza di risultati corretta, la Web application esegua query differenti a seconda di quale deve essere il primo elemento della pagina e di quanti elementi desideriamo visualizzare nella singola pagina. Infatti, andando a sbirciare nel codice HTML, nel form generato per costruire la pagina successiva di risultati, notiamo il seguente campo hidden: <input type="hidden" name="start_row" value="11"><input type="hidden" name="sql" value="SELECT TOP 20 * FROM Prodotti WHERE Descrizione LIKE '%pippo%' ORDER BY id">.
Si può facilmente immaginare che cosa succederebbe se un utente del sito salvasse la pagina sul proprio hard disk e la modificasse come segue <input type="hidden" name="sql" value="DELETE FROM Prodotti">.
Questo caso è riscontrato più spesso sugli script di ricerca a più pagine, ma numerosi sono le occorrenze in cui, all'interno del codice HTML delle pagine Web (non necessariamente di ricerca multipagina), si può trovare del codice SQL esposto alla modifica da parte dell'utente.
Visti i fallimenti esposti negli esempi precedenti, i progettisti della nostra applicazione Web decidono di riconoscere l'utente attraverso un cookie a cui è associata (lato server) una struttura contenente variabili di sessione. In questo modo sarà possibile evitare di passare informazioni importanti tramite l'URL o mediante l'uso di campi nascosti. Diciamo che quando l'utente viene autenticato, il server gli invia un cookie contenente l'id dell'utente che è stato verificato. Da quel momento in poi, il server riconoscerà quel client attraverso tale cookie, assegnandogli diritti e privilegi in base a quanto stabilito nel database per l'utente con quell'id. Una domanda sorge spontanea: anche gli utenti con privilegi amministrativi saranno nello stesso database? È probabile. Quindi modifichiamo il nostro cookie (è un comune file di testo) e cambiamo l'id con uno dei primi. È infatti abitudine degli amministratori auto-assegnarsi un id utente tra i primi disponibili. Ed ecco, infatti, che quando il nostro cookie assume il valore 11, il sito ci riconosce come administrator e diventiamo utenti plenipotenziari. Possiamo fare ciò che vogliamo, amministrare il sito, i contenuti, il database, creare, modificare o cancellare qualunque cosa. Ovviamente si tratta di un caso limite, ma è comunque esplicativo di quali potenzialità offra la manipolazione di questi elementi. Questo procedimento è conosciuto col nome di cookie tampering o anche cookie poisoning.
La minaccia affonda le proprie radici non già nella pura tecnologia, bensì nello sfruttamento di tutte quelle leggerezze o negligenze commesse dai progettisti e dai programmatori che realizzano le applicazioni Web. Da parte di chi è preposto alla realizzazione di tali strumenti è da caldeggiare l'adozione di tutti quei criteri di obfuscation tesi a evitare che qualunque frammento di codice possa essere in qualsiasi modo manipolato dagli utenti dell'applicazione.
Volendo semplificare e indicare solo alcuni punti cruciali in modo schematico:
In fin dei conti questa è una delle differenze principali che determinano la qualità di una Web application e conseguentemente la professionalità di chi la realizza.
La realizzazione di applicazioni Web più sicure comporta un maggior dispendio di tempo e di risorse e, conseguentemente, un maggior costo per il committente. Ma comprereste mai un'automobile sapendo che il produttore ha montato un serbatoio che potrebbe esplodere, solo perché costa meno di un'auto sicura?