The Deep Tour: Prologo

Come tutti sappiamo, la rete globale ha un’estensione gigantesca e l’internet è un mondo davvero grande. E come ogni grande mondo anche esso nasconde segreti e posti oscuri. Ed è così che tutti ne parlano ma nessuno sa effettivamente cosa sia: parliamo infatti del Deep Web.
Il Deep Web o anche detto DarkNet è costituito da tutti i siti e servizi online che nostri comuni motori di ricerca non riportano nei risultati delle ricerche. Ma noi ci siamo stati e vi spiegheremo perché e sfateremo anche alcuni miti su questo mondo poco conosciuto.

Una delle più comuni rappresentazioni del Deep Web è la metafora di ciò che l’iceberg fa vedere e quello che è effettivamente invisibile a chi “naviga” nel web

Leggi tutto “The Deep Tour: Prologo”

Ada vs Injection Attacks

Secondo l’OWASP (Open Web Application Security Project) in testa alla top 10 dei più comuni bachi di sicurezza del web c’è l’injection attack, un tipo di attacco reso possibile dall’uso imprudente di dati ricevuti dall’utente.  In questo articolo mostriamo come proteggersi — a costo zero — dall’injection attack sfruttando la caratteristica di Ada di essere strongly typed.

Il problema: dati esterni &  injection attack

Avete appena avuto l’idea che vi farà ricchi: un sito web attraverso il quale vendere biciclette per cincillà (originale, non c’è che dire… forse un po’ di nicchia).  Vi create la vostra applicazione che include una pagina di login per i vostri numerosi (speriamo) clienti

Dal lato server, per autenticare il cliente,  interrogate il vostro database con qualcosa del tipo

$query = "SELECT pwd FROM clienti WHERE user='" . $_GET["usr"] . "';"
$password = $db->query($query)

Andate a dormire sognando di fare il bagno nell’oro come Paperone e la mattina dopo vi svegliate e… Il database non c’è più!  Cosa è successo?  Forse qualcosa di simile a questo

In effetti, usando come username lo strano nome nel fumetto si ottiene la query

SELECT pwd FROM clienti WHERE user='Robert'; DROP TABLE clienti; -- ';

Anche senza avere una conoscenza approfondita dell’SQL, si capisce che tale comando, oltre ad interrogare la tabella clienti, la cancella…

Questo tipo di attacco si chiama SQL Injection ed è un caso specifico di Injection attack, una delle cause più comuni di problemi di sicurezza su web. L’injection attack è reso possibile quando un dato proveniente dall’esterno (es. ricevuto da un utente, letto da un file, …) viene passato direttamente ad un interprete (shell, MySQL, …)  senza prima “depurarlo” (per esempio, quotando eventuali caratteri speciali).

In alcuni casi, come quello dell’esempio, la possibilità di un attacco di questo tipo è evidente, ma possono esistere altri casi in cui il problema è molto più nascosto se, per esempio, la query dipende dall’input dell’utente in maniera indiretta (ossia, usa dati che sono stati ricavati in un’altra porzione del codice dall’input dell’utente).

L’idea di dato contaminato (taint)

Per risolvere questo tipo di problema alcuni linguaggi (es. Ruby) introducono l’idea di dato taint (“contaminato”). Un dato letto dall’esterno è considerato “sporco” e non può essere usato con funzioni pericolose quali query o eval. Inoltre, la contaminazione è contagiosa e un risultato ottenuto elaborando un dato contaminato è contaminato a sua volta. Prima di poter passare un dato contaminato ad una funzione pericolosa è necessario dichiarare il dato  “pulito” usando —tipicamente— una specifica funzione fornita dal linguaggio (es. untaint in Ruby). Ovviamente, si suppone che il programmatore non sia pigro ed effettivamente “depuri” il dato (per esempio, quotando eventuali caratteri speciali) prima di dichiararlo pulito.

 

L’idea di dato contaminato è  chiaramente uno strumento molto utile per evitare code injection, ma è uno strumento che si presta più per i linguaggi interpretati che compilati. In molti linguaggi compilati quali C, C++, … la soluzione più semplice è forse la formulazione di opportune regole di programmazione (chiama system() solo con argomenti decontaminati!)  con pene esemplari (di fantozziana memoria) per chi sgarra

Static tainting in Ada

Anche se Ada può non sembrare una scelta intuitiva per un applicativo web, l’esistenza della libreria Ada Web Server (AWS, nulla a che fare con Amazon…) permette di implementare facilmente applicazioni robuste con interfaccia web. In particolare, per quanto riguarda la protezione da injection attack, in Ada è possibile sfruttare l’idea di dato contaminato in maniera statica (ossia verificata al momento della compilazione e non a runtime), senza overhead in fase di esecuzione.




Si consideri, per esempio il seguente “esempio giocattolo” di un package che esporta funzioni per interrogare un database

package DB_Queries is
   type Safe_Query (<>) is private;

   function Purify (Item : String) return Safe_Query;

   function MySQL_Query (Query : Safe_Query) return String;

   function "&"(Left, Right : Safe_Query) return Safe_Query;

   function "&"(Left  : Safe_Query; Right : String) 
                return Safe_Query
   is (Left & Purify(Right));  -- Syntactic sugar

   function "&"(Left  : String; Right : Safe_Query) 
                return Safe_Query
   is (Purify(Left) & Right);  -- More syntactic sugar
private
   type Safe_Query is new String;
end DB_Queries;

Un paio di osservazioni per chi non è familiare con Ada

  • In Ada  i package sono un po’ lo strumento base per organizzare il codice.  Un package tipicamente esporta una o più funzioni/procedure per operare sui tipi.  Ogni package ha un body che contiene il codice eseguibile (una specie di  *.c, insomma) ed una spec che contiene la dichiarazione delle risorse esportate verso l’esterno (all’incirca l’equivalente di un *.h).  Ogni file di spec ha poi due parti: una parte pubblica ed una parte privata, separate dalla keyword private.  Ovviamente solo la parte pubblica è visibile alla parte di  codice esterna al package.
  • La decorazione “(<>)” nella definizione pubblica di Safe_String sta ad indicare che Safe_String è un indefinite type. Spiegare nel dettaglio cosa sia un tipo indefinito ci porterebbe troppo lontano, diciamo semplicemente che si tratta di un tipo che non può essere allocato senza fornire altri dati.  Per esempio, per definire una variabile di tipo String (che è un array di caratteri) bisogna fornire la sua lunghezza, così che non posso scrivere
    X : String;  -- Quanto lunga deve essere?

    ma devo scrivere

    X : String(1..10);       -- Stringa di 10 caratteri
    Y : String := Get_Line;  -- OK, Y inizializzata col risultato di Get_Line

Nel codice sopra vediamo all’inizio la dichiarazione del tipo Safe_String che rappresenta una stringa di testo “ripulita” in modo da poter essere usata in una query SQL.  Poiché il tipo è dichiarato private, la sua struttura interna non è visibile al di fuori del package. La dichiarazione  completa di Safe_String si trova nella parte privata ed è

type Safe_String is new String;

Tale dichiarazione afferma che Safe_String è… semplicemente una stringa, ma diversa…  Il compilatore genererà quindi per Safe_String lo stesso codice che avrebbe generato per una stringa normale, ma impedirà al programmatore di mescolare direttamente i due tipi.

Questo è un aspetto importante di Ada: in C Safe_String sarebbe un sinonimo di String e il programmatore potrebbe mescolare i due tipi tra loro; in Ada Safe_String e String sono considerati due tipi diversi che non possono essere mescolati, anche se la loro implementazione interna è la stessa.  Questo tipo di separazione, se usato correttamente, è un potente strumento per ridurre il numero di errori nel codice (been there, done that…).

Più avanti vediamo la dichiarazione della funzione

function MySQL_Query (Query : Safe_Query) return String;

che, intuiamo, manda una query al database.  Si osservi che non possiamo interrogare il database con una stringa qualsiasi, ma solo con una Safe_Query, ossia una stringa che è garantita essere esente da injection attack.  Se provassimo a chiamare

Result := MySQL_Query
         ("SELECT * FROM t WHERE usr='" & Get_Param("usr") & "'" );

riceveremmo un errore in fase di compilazione poiché stiamo chiamando MySQL_Query con il tipo sbagliato, ossia una String e non una Safe_String.  L’unico modo per creare una stringa sicura è attraverso la funzione

  function Purify (Item : String) return Safe_Query;

che —possiamo supporre— verifica la presenza di caratteri speciali e,  eventualmente, li quota.  Questa funzione è l’unico cancello attraverso il quale deve passare ogni stringa prima di poter  essere usata come query, così che il programmatore non può, nemmeno volendo, passare una stringa pericolosa alla funzione MySQL_Query.

Onde permettere un minimo di elaborazione con le Safe_Query il package definisce anche tre operatori di concatenazione

function "&"(Left, Right : Safe_Query) return Safe_Query;

function "&"(Left  : Safe_Query; Right : String) 
             return Safe_Query
is (Left & Purify(Right));  -- Syntactic sugar

function "&"(Left  : String; Right : Safe_Query) 
             return Safe_Query
is (Purify(Left) & Right);  -- More syntactic sugar

Il primo può essere implementato come un alias della normale concatenazione tra stringhe (in fondo una Safe_String è pur sempre una stringa…), gli altri due permettono la concatenazione tra una stringa normale e una sicura chiamando “dietro le quinte” la funzione Purify. Come specificato dal commento, gli ultimi due operatori di concatenazione sono semplicemente zucchero sintattico, ma sono convenienti perché permettono di scrivere codice come

Result := MySQL_Query
          ("SELECT * FROM t WHERE usr='" & Purify(Get_Param("usr")) & "'" );
Si osservi come  gli operatori di concatenazione scelti dal compilatore per tradurre la linea sopra siano necessariamente quelli definiti nel package (e non l’operatore “standard” di concatenazione tra stringhe) poiché Purify restituisce una Safe_String.
La sintassi per la definizione di funzione usata nell’esempio (introdotta in Ada 2012 ) è detta espression function ed è molto conveniente.

 

Altri approcci

L’approccio presentato qui è forse il più semplice, ma non è l’unico. Due possibili esempi di approcci alternativi sono l’uso dei dynamic predicate e l’uso di package privati per controllare l’accesso a porzioni “rischiose” del  codice. Descriverli qui rischierebbe però di appesantire eccessivamente questo articolo e ne parleremo più diffusamente in un’altra occasione.

Computer Crimes: I reati informatici in Italia

Al giorno d’oggi sentiamo parlare quasi ogni giorno di cyber-terrorismo, attacchi informatici a infrastrutture e siti, pirateria e furti di credenziali. A partire dagli anni ’70 la conoscenza informatica ha iniziato a diffondersi a macchia d’olio dai laboratori alle istituzioni pubbliche fino ad arrivare nelle case di tutti noi. Ma come nel mondo reale esiste la criminalità, anche il mondo virtuale ha conosciuto le cattive intenzioni di molti individui, desiderosi di ottenere e manipolare informazioni riservate o semplicemente causare danni e disagi ad altri utenti. Se all’inizio la maggior quantità di dati e informazioni era contenuta solamente all’interno di mainframe conservati in aziende, banche e tribunali, oggi grazie a Internet e alla rete, chiunque può accedere a qualsiasi informazione sfruttando metodi e falle nella protezione dei dati stessi. Appare chiaro quindi che deve esistere una legislazione chiara che punisca tali azioni in quanto veri e propri reati. Reati informatici per la precisione.

I mainframe concentrano le informazioni in un solo luogo impedendo l’accesso dall’esterno. Ma se il vero pericolo fosse all’interno?

Il primo segnale di risposta ai crimini informatici in Europa arriva nel 1989 con la Raccomandazione numero 9 al fine di garantire una corretta protezione dei sistemi informatici da parte delle continue minacce di intrusione.




In Italia invece bisognerà aspettare il 1993 dove la Legge 547 definisce e punisce per la prima volta i reati informatici: al tempo non si scrisse un codice a parte ma il legislatore decise di contemplare tutte le ipotesi di reato (e le relative pene) integrandole nell’attuale codice penale, affiancandole ai reati già esistenti. Ad esempio l’Art. 615 Ter punisce l’accesso abusivo a sistemi informatici paragonandolo alla violazione di domicilio! Si rischia infatti la reclusione fino a 3 anni (fino a 8 con eventuali aggravanti). L’Art. 615 Quater invece punisce la diffusione o detenzione abusiva di codici di accesso a sistemi informatici, punendo in modo particolare non l’accesso ai sistemi ma le intenzioni. In ogni caso si rischia la reclusione per 1 anno ed una multa salatissima.

copy-of-houston_texas_fort_bend_harris_montgomery_galveston_cyber_internet_crime_fraud_theft_lawyer-e1433189418483

Più severo è l’Art. 616 ovvero la violazione di corrispondenza sia cartacea che informatica. Intercettare comunicazioni tra i sistemi infatti può essere punito con la reclusione da 6 mesi a 4 anni! Non è da meno l’Art. 635 bis che riguarda il danneggiamento di dati o programmi altrui, punito con la reclusione fino a 3 anni. Attenzione quindi a non toccare il codice dei vostri colleghi senza la loro autorizzazione!

Se i reati informatici in Italia sono stati definiti nel ’93 bisognerà aspettare il 2008 per l’integrazione effettiva nel codice di procedura processuale che stabilirà i metodi di indagine per provare questi crimini. Ma le tecniche e i metodi per aggirare protezioni senza lasciare traccia migliorano molto più velocemente dei modi per impedire e prevenire tutto questo.

Gestite con attenzione i vostri dati, sia a livello locale che sul web, il pericolo è dietro l’angolo!