università politecnica delle marche facoltà di ingegneria
Transcript
università politecnica delle marche facoltà di ingegneria
UNIVERSITÀ POLITECNICA DELLE MARCHE FACOLTÀ DI INGEGNERIA Corso di Laurea Triennale in Ingegneria Elettronica GESTIONE DELLA SICUREZZA DI UN PORTALE WEB MEDIANTE CONTROLLO CONDIZIONATO DEGLI ACCESSI E CRIPTAZIONE DEI DOCUMENTI DEPOSITATI Relatore: Prof. Aldo Franco Dragoni Anno Accademico 2006/2007 Rapporto Finale di: Alessandro Rossi INDICE INTRODUZIONE 1 CAPITOLO 1 2 1.1 La sicurezza 1.1.1 La sicurezza su Internet 3 3 1.2 Aspetti giuridici sulla sicurezza 1.2.1 Un quadro comunitario per le firme elettroniche 1.2.2 Il nuovo “testo unico sulla privacy” 4 4 5 1.3 La crittografia 1.3.1 La crittografia simmetrica 1.3.1.1 Cifratura a blocchi 1.3.1.2 Cifratura a flusso 1.3.1.3 I principali algoritmi a chiave simmetrica 1.3.2 La crittografia asimmetrica 1.3.2.1 Algoritmi a chiave di cifratura pubblica 1.3.2.2 Algoritmi a chiave di decifratura pubblica 1.3.2.3 Algoritmi ibridi 1.3.2.4 I principali algoritmi a chiave asimmetrica 1.3.3 Stima sulla sicurezza degli algoritmi crittografici 1.3.4 La funzione di hash 1.3.4.1 I principali algoritmi per la funzione di Hash 1.3.4.2 Stima sulla sicurezza della funzione di Hash 1.3.5 La firma digitale 6 8 9 10 10 11 12 13 14 15 16 18 19 19 20 1.4 La public key infrastructure 1.4.1 Il certificato a chiave pubblica 1.4.1.1 Richiesta, ottenimento, durata e revoca di un certificato 1.4.2 Autorità di certificazione 1.4.3. Standard X.509 1.4.4 Standard PKCS 23 23 25 27 28 30 I 1.5 La steganografia 1.5.1 Steganografia iniettiva e generativa 1.5.2 Modelli steganografici 1.5.2.1 Steganografia sostitutiva 1.5.2.2 Steganografia selettiva 1.5.2.3 Steganografia costruttiva 1.5.3 La steganografia applicata ai files digitali 1.5.3.1 Steganografia dei files Bitmap 1.5.3.2 Steganografia dei files Wave 1.5.3.3 Steganografia dei files compressi 1.5.4 Stima sulla sicurezza degli algoritmi steganografici 32 33 33 33 34 35 35 36 41 44 47 1.6 I protocolli web 1.6.1 Il protocollo http 1.6.2 Il protocollo https 49 49 54 CAPITOLO 2 61 2.1 La Phi.d’αlpha s.r.l. 62 2.2 Descrizione del progetto 63 CAPITOLO 3 64 3.1 Analisi delle specifiche 65 3.2 Modellazione dei pericoli 65 3.3 Anali delle contromisure 3.3.1 Uso insicuro della crittografia 3.3.2 Controlli di accesso insufficienti 3.3.3 Iniezioni di comandi 3.3.4 Gestione insicura delle sessioni 3.3.5 Gestione inadeguata degli errori 68 69 70 72 72 72 3.4 Implementazione del software lato client 3.4.1 Software per la gestione della crittografia 3.4.2 Software per la gestione della steganografia 73 73 75 3.5 Implementazione dell’applicazione web 3.5.1 Login e logout degli utenti 3.5.2 Upload e download dei files 80 81 83 II CONCLUSIONI E SVILUPPI FUTURI 87 APPENDICE 89 Codice VB.NET del software di crittografia Codice VB.NET del software di steganografia Codice ASP del software web BIBLIOGRAFIA 90 96 104 120 III INTRODUZIONE Nel settembre del 1969, negli USA, l’ARPA (Advanced Research Projects Agency), sotto il controllo del Dipartimento della Difesa degli Stati Uniti, diede vita ad una rete di computer: ARPANET. Negli anni successivi il progetto subì un enorme ampliamento fino alla definizione del protocollo TCP/IP che segnò l’inizio effettivo di quello che oggi viene definita “Internet”. Quando il protocollo TCP/IP fu progettato, la rete collegava tra loro università e laboratori di ricerca ed era uno strumento per la condivisione delle risorse e delle informazioni da parte della comunità scientifica, in virtù di ciò gli utenti della rete erano una comunità ristretta e fidata, e per questo non si fece particolare attenzione alle problematiche relative alla sicurezza. Oggi Internet è diventato uno strumento di comunicazione globale, quindi la situazione è molto diversa e il tema della sicurezza in rete assume una grande importanza, basti pensare che anche la giurisprudenza italiana con il D.L. 30 giugno 2003, n. 196 fissa delle regole concrete sul trattamento dei dati ai fini della sicurezza e della privacy e sancisce regole da applicarsi alla trasmissione dei documenti informatici e alla firma digitale con il D.P.R. 10 novembre 1997, n. 513. In questa tesi verrà progettato un portale web in cui viene garantito un elevato grado di sicurezza mediante il controllo condizionato degli accessi e la criptazione dei documenti depositati. Per lo scambio delle chiavi segrete, necessarie per l’utilizzo di tecniche di crittografia, verrà introdotto un approccio all’uso della steganografia. Per la realizzazione del progetto si utilizzeranno diversi linguaggi di programmazione: • Visual Basic Script; • Java Script; • ASP; • Visual Basic .NET. Le pagine web verranno create tramite il linguaggio di mark-up HTML. -1- CAPITOLO 1 In questo capitolo verrà descritto che cosa si intende per sicurezza su Internet, dopodichè verranno forniti dei cenni sulla crittografia, sulle public key infrastructure, sulla steganografia e sui protocolli per il web. -2- 1.1 La sicurezza Che cosa si intende con il termine sicurezza? Se consultiamo il dizionario Treccani (www.treccani.it) vediamo che per sicurezza si intende: “il fatto di essere sicuro, come condizione che rende e fa sentire d’essere esente da pericoli, o che dà la possibilità di prevenire, eliminare o rendere meno gravi danni, rischi, difficoltà, evenienze spiacevoli, e sim”. 1.1.1 La sicurezza su Internet Per quanto riguarda Internet, o una qual si voglia rete di computer, tale termine deve essere usato per commisurare il grado di immunità ai rischi che possono presentarsi, in quanto una rete completamente sicura non esiste e forse non esisterà mai. Per garantire la sicurezza di una rete, ci si trova a dovere fronteggiare aspetti e problematiche diverse, per risolvere le quali si usano approcci differenti. I principali servizi per la tutela della sicurezza sono i seguenti: • autenticazione d’identità: garantire l'identità degli interlocutori; • autenticità dei dati: garantire i dati identificativi del mittente; • riservatezza: impedire letture non autorizzate, garantendo la segretezza del contenuto della comunicazione; • integrità: garantire che dati e informazioni non subiscano modifiche non autorizzate, in particolare durante il transito dal mittente al destinatario; • non ripudio: impedire che le parti neghino di aver inviato (o ricevuto) dati che hanno effettivamente inviato (o ricevuto); • autorizzazione: permettere l'accesso a determinate risorse o servizi solo a chi è autorizzato a farlo; • disponibilità: garantire l'operatività di un servizio e la sua fruibilità da parte degli utenti autorizzati, evitando che azioni di disturbo possano compromettere la disponibilità del sistema. Gli attacchi alla sicurezza di un sistema possono essere dunque molteplici, colpendo l'uno o l'altro di questi aspetti. Un intruso può ad esempio intercettare dei dati (colpendo quindi la riservatezza), modificare le informazioni in transito (integrità), sostituirsi ad uno degli interlocutori (autenticazione), rendere indisponibile un servizio. Un attacco classico è il cosiddetto “man-in-the-middle” (MITM), in cui un intruso si inserisce nella comunicazione impersonando ciascun interlocutore agli occhi dell'altro, e può così leggere tutti i dati scambiati, modificarli, inserirne altri. -3- 1.2 Aspetti giuridici sulla sicurezza Dimostrare l’autenticità di un documento, sia esso giuridico o privato, dipende dal fatto di poter palesare la sua inalterabilità o delle eventuali tracce che hanno portato ad una modifica. Per quanto riguarda i documenti cartacei questo avviene tramite l’apposizione di una firma autografa. Con l’avvento e la successiva espansione dei sistemi informatici, la giurisprudenza si è trovata di fronte al problema di dover produrre una normativa capace di poter efficacemente convalidare l’autenticità di un documento. Un altro punto focale che i legislatori hanno dovuto risolvere è stato quello di sancire delle regole affinché sia tutelato il tema della riservatezza. 1.2.1 Un quadro comunitario per le firme elettroniche In questo paragrafo verrà descritto l’iter legislativo che ha portato all’equiparazione del documento informatico firmato digitalmente alla scrittura privata. In materia di formazione, archiviazione e trasmissione di atti, documenti e contratti in forma elettronica, fino al 1997, esisteva un quadro normativo alquanto frammentario e generico. Il 15 marzo 1997 con la legge n. 59, detta legge Bassanini, il legislatore italiano ha realizzato una vera e propria rivoluzione di diritto pubblico e di diritto privato introducendo nell'ordinamento la nozione di documento informatico e telematico, provvedendo a dettare una specifica disciplina in materia. All'art. 15, secondo comma, dispone “gli atti, i dati e i documenti formati dalla pubblica amministrazione e dai privati con strumenti informatici e telematici, i contratti stipulati nelle medesime forme, nonché la loro archiviazione e trasmissione con strumenti informatici, sono validi e rilevanti a tutti gli effetti di legge”, e rinvia a successivi regolamenti la precisazione sulle modalità di applicazione della legge, sia per la Pubblica Amministrazione sia per i privati. Uno di questi regolamenti è il D.P.R. n. 513 del 10 novembre 1997, finalizzato alla creazione di un sistema di firma sicura, che disciplina i criteri e le modalità per la formazione, l'archiviazione e la trasmissione di documenti con strumenti informatici e telematici. La caratteristica principale del decreto è costituita dalla definizione di documento informatico e dalla precisa determinazione della sua validità. Il D.P.R. n. 513 stabilisce indirettamente l’equivalenza tra documento informatico e documento cartaceo, infatti l’equiparazione degli effetti probatori del primo rispetto al secondo è subordinata alla conformità del documento (informatico), alle disposizioni del regolamento e delle regole tecniche che sono state emanate con D.P.C.M. 8 febbraio 1999. -4- Le norme di questo decreto sono state successivamente incorporate nel D.P.R. n. 445 del 28 dicembre 2000 detto “testo unico sulla documentazione amministrativa”. L'ultima tappa è rappresentata dal D.Lgs. n. 10 del 23 gennaio 2002, attuativo della direttiva n. 1999/93/CE, relativa a “un quadro comunitario per le firme elettroniche”, il quale ha completamente stravolto il sistema esistente affiancando la firma elettronica a quella digitale. 1.2.2 Il nuovo “testo unico sulla privacy” Come in precedenza accennato, anche la giurisprudenza italiana, ha disciplinato, il tema della sicurezza nel trattamento dei dati. In questo paragrafo verrà decritto il D.L. 30 giugno 2003, n. 196, entrato in vigore il 1 Gennaio 2004 sostituendo la legge 675/1996 e successivi decreti legislativi, detto anche “testo unico sulla privacy”. Tale legge si rivolge a tutti coloro che trattano dati personali di clienti, fornitori, dipendenti, utenti, pazienti, colleghi, soci o associati. Quindi vengono coinvolte aziende, liberi professionisti, amministrazioni pubbliche, associazioni e cooperative. La legge sancisce una serie di regole e procedure gestionali rivolte a garantire la sicurezza dei dati personali all’interno delle aziende, attraverso l’adozione di requisiti minimi di sicurezza che dovranno essere utilizzati nell'espletamento delle suddette attività. Con scadenza ultima al 31 Dicembre 2004 tutte le aziende dovranno adeguarsi alle disposizioni imposte dal D.L. 30 giugno 2003, n. 196 e redigere il Documento Programmatico sulla Sicurezza in cui dovranno essere riassunte le misure a cui saranno vincolati ad adottare per garantire la sicurezza dei dati. Per mancato adempimento di tale obbligo sono previste sanzioni pecuniarie e penali, con multe che possono arrivare fino 60.000 Euro e sanzioni penali che possono raggiungere la pena di tre anni di reclusione. Per adeguarsi alle disposizioni del nuovo codice sulla privacy, le attività interessate dovranno, adottare una modulistica, individuare i soggetti incaricati del trattamento dei dati e verificare l’adeguamento dei propri sistemi informatici secondo quanto previsto dall'Allegato B (Disciplinare tecnico in materia di misure minime di sicurezza). Paul Baran in un rapporto per la Rand Corporation sancisce l’importanza dell’uso delle pratiche di sicurezza, anche se non imposte da un sistema giuridico, dicendo: "...non aspettiamoci che il contributo dei giuristi possa sostituire una buona progettazione tecnica, anche se non si volesse tenere conto del ritardo sociale dei procedimenti legislativi e giudiziari, gli specifici problemi del mondo dei computer si collocano in una dimensione che ad essi, ai giuristi, sfugge completamente...". -5- 1.3 La crittografia L’etimologia del termine “crittografia” è data dall’associazione di due parole di origine greca: “criptos” che significa nascondere, e “grafèin” che significa scrivere, da cui “scrivere di nascosto”. L’arte di scrivere messaggi segreti che possano essere letti e compresi solo dal destinatario risale alla più remota antichità, già la Bibbia parla di un codice segreto per scrivere il nome di Babele, il codice “Atbash”, esso consisteva nell’inversione delle lettere alfabetiche, ovvero la “a” diventava “z”, la “b” diventava “y” e così via, fino a giungere ai tempi più recenti in cui durante la seconda guerra mondiale i tedeschi avevano ideato “Enigma” un algoritmo simmetrico per trasmettere in modalità protetta i propri messaggi, in cui ogni mese le chiavi venivano consegnate ai cifratori scritte in un foglio con inchiostro solubile che doveva essere custodito gelosamente dagli stessi. Figura 1a. Cifrario Atbash Figura 1b. Soldati tedeschi che lavorano su Enigma Per secoli quest’arte è stata appannaggio quasi esclusivo dei militari e dei diplomatici ed i metodi crittografici erano specifici per l’invio di messaggi materiali che venivano affidati ai corrieri. In contrapposizione e non solo, nasce contemporaneamente alla crittografia una nuova disciplina che prende il nome di “crittoanalisi” che studia come recuperare il contenuto di un messaggio cifrato senza disporre delle informazioni necessarie alla decifratura. La crittografia e la crittoanalisi si basano su una branca della matematica che prende il nome di “crittologia”. L’evoluzione delle tecniche crittografiche ha avuto una svolta quando si è deciso di rendere pubblici gli algoritmi di cifratura e decifratura dei messaggi; infatti la -6- pubblicazione degli algoritmi ha reso possibile lo studio da parte dei crittoanalisti, consentendo di scoprirne eventuali debolezze e quindi di selezionare gli algoritmi più sicuri, e ha reso possibile la fruizione degli stessi a chiunque, anche a coloro inesperti di scienza crittologica. La crittografia moderna è in grado di garantire i seguenti servizi di sicurezza: • autenticazione d’identità: verifica dell’identità di una o entrambe le parti coinvolte in una comunicazione; • integrità dei dati: il documento trasmesso deve contenere tutte le informazioni in esso presenti sin dalla redazione, con la possibilità di verificare le eventuali alterazioni, intenzionali o meno, che i dati possono aver subito attraversando un canale insicuro; • autenticità dei dati: verifica dell’effettiva identità del mittente; • non ripudio: impossibilità, da parte sia del mittente che del destinatario, di negare l’avvenuto scambio di dati. La comunicazione fra due entità, “mittente” e “destinatario”, può avvenire in due modi: • comunicazione in modo chiaro: Figura 2. Schema di principio di una comunicazione in chiaro fra due entità Il messaggio viene inviato dal mittente verso il destinatario attraverso il canale in modo trasparente (privo di cifratura). Lungo il canale il messaggio è visibile a tutti e questo va a compromettere la sicurezza della trasmissione • comunicazione in modo cifrato: Figura 3. Schema di principio di una comunicazione cifrata fra due entità -7- Il messaggio in chiaro viene innanzitutto cifrato per mezzo di una chiave dal mittente, il quale lo trasmette attraverso il canale al destinatario. Una volta giunto a quest’ultimo, il destinatario tramite l’apposita chiave decifrerà il messaggio, ottenendo il messaggio in chiaro originale. Lungo il canale il messaggio è visibile a tutti ma questo non compromette la sicurezza della trasmissione in quanto, essendo cifrato, solo chi è in possesso dell’apposita chiave è in grado di comprenderne il significato. 1.3.1 La crittografia simmetrica La crittografia simmetrica utilizza algoritmi che si avvalgono di un’unica chiave sia per le operazioni di cifratura sia per quelle di decifratura, per questo viene detta anche a chiave segreta. Lo schema di principio di una comunicazione con crittografia a chiave simmetrica è il seguente: Figura 4. Schema di principio di una comunicazione cifrata con crittografia a chiave simmetrica Il mittente prima dell’invio del messaggio genera una chiave con cui cifra il messaggio e lo manda al destinatario tramite un canale insicuro; quindi chiunque può leggere il messaggio ma non avendo la chiave di decodifica non può interpretarlo. La chiave che ha generato il mittente e che serve al destinatario per decifrare il messaggio viene mandata al destinatario attraverso un canale sicuro in modo tale che nessuno possa intercettarla e leggerla. La sicurezza di questa tecnica crittografica è garantita fintantoché la chiave rimane a conoscenza dei due soli interlocutori. -8- Il difetto principale della crittografia simmetrica sta nel fatto che anche se si adotta un canale sicuro, la trasmissione della chiave può essere comunque intercettata. Nel caso in cui un terzo interlocutore, non desiderato, venisse in possesso della chiave, andrebbe a provocare il decadimento di alcuni servizi di sicurezza che invece la trasmissione criptata dovrebbe garantire, quindi verrebbero meno i requisiti di segretezza, di integrità del messaggio e di autenticazione del mittente, infatti la compromissione del terzo interlocutore in possesso della chiave, potrebbe consistere nell’intercettazione del messaggio cifrato, nella modifica e cifratura di uno nuovo, senza che il destinatario si accorga di nulla. Un altro svantaggio della crittografia simmetrica riguarda il fatto che ogni utente deve disporre di una chiave diversa per ogni interlocutore con cui voglia comunicare, sapendo che il numero di chiavi cresce con legge quadratica, nella nostra situazione, ponendo n come numero di presunti interlocutori, un utente si n ( n − 1) troverebbe a gestire chiavi segrete, quindi se il numero di interlocutori è 2 estremamente elevato, l’utente si trova a gestire un’infinità di chiavi. Gli algoritmi di crittografia simmetrica si possono suddividere in due tipologie in base alla tecnica utilizzata per la cifratura: • algoritmi a blocchi: l’operazione di cifratura o decifratura viene effettuata su gruppi di bit, byte o word di lunghezza finita organizzati in blocco; • algoritmi a flusso: l’operazione di cifratura o decifratura viene effettuata sui singoli bit, byte o word. 1.3.1.1 Cifratura a blocchi Negli algoritmi a blocchi il messaggio da cifrare, o decifrare, viene suddiviso in blocchi di lunghezza prefissata e l’algoritmo viene applicato ad ognuno di questi blocchi singolarmente. Visto ciò risulta ovvio che il messaggio debba avere una lunghezza esattamente multipla della dimensione del blocco, altrimenti si andrebbe a verificare un errore; per ovviare a questa anomalia si provvede a riempire l’ultimo blocco con opportuni caratteri di allineamento. Questa operazione è nota con il termine di “pad”. Esistono diversi metodi per eseguire il “pad” di un blocco, essi si diversificano in base alla tipologia di dato che contengono. Di seguito si elencheranno le metodologie di “pad” più comuni, ma in realtà ne esistono innumerevoli altre. Il metodo più semplice è quello di aggiungere zeri fino al raggiungimento della lunghezza prefissata del blocco; se i dati sono binari si completa il blocco con bit che sono l’opposto degli ultimi bit costituenti il messaggio; nel caso di caratteri ASCII si usano byte di riempimento casuali specificando nell’ultimo byte il carattere ASCII corrispondente al numero di byte aggiunti. -9- 1.3.1.2 Cifratura a flusso Negli algoritmi a flusso, il messaggio in chiaro si mostra come uno stream di dati e l’operazione di cifratura, o decifratura, viene effettuata su ogni singolo bit, byte o word, in ingresso. Questa tipologia di algoritmi fa uso di un generatore di bit che produce una sequenza di bit pseudo-casuale in relazione alla chiave fornita dagli utenti. Essenziale risulta il generatore pseudo-random, in quanto in fase di cifratura i dati in ingresso vengono messi in XOR con i dati provenienti dal generatore, questo per fare in modo che messaggi identici cifrati con la stessa chiave non diano mai origine a messaggi cifrati uguali. Gli algoritmi a flusso possono essere suddivisi in due categorie: • algoritmi autosincronizzanti: in questo tipo di algoritmi il generatore di numeri pseudo-random prende in ingresso, ad ogni step, anche un certo numero di bit cifrati precedentemente, quindi, qualunque sia la chiave segreta, uno stesso bit, o byte, potrà essere cifrato ogni volta in modo diverso a seconda delle precedenti informazioni elaborate dal flusso in ingresso. • algoritmi sincroni: in questo tipo di algoritmi, il generatore pseudorandom è completamente indipendente dal flusso di dati in ingresso. Gli algoritmi sincroni risultano essere molto vantaggiosi su canali di trasmissioni rumorosi, in quanto riducono la propagazione degli errori, si pensi per esempio alla trasmissione di un messaggio in un canale rumoroso. Quando il ricevitore riceverà un bit errato, dovuto alla rumorosità del canale, l’errore non si ripercuoterà su altri bit, in quanto non c’è la dipendenza dagli stati precedenti, come avviene negli algoritmi autosincronizzanti. 1.3.1.3 I principali algoritmi a chiave simmetrica I principali algoritmi a chiave simmetrica sono: • DES (Data Encryption Standard): fu sviluppato dalla IBM come evoluzione dell’algoritmo Lucifer, risalente agli anni ’70. Fu il primo algoritmo di cifratura a diventare uno standard nazionale negli Stati Uniti nel 1977. Il DES rientra nella classe dei codici a blocchi, ogni blocco ha la dimensione di 64 bit e la chiave di cifratura è di 56 bit. Il DES fu violato per mezzo di un attacco di forza bruta nel 1998, quindi risulta essere poco robusto per l’utilizzo odierno. • TDES (Triple DES): è una evoluzione del DES, è così denominato in quanto utilizza tre chiavi indipendenti in tre diversi passaggi: una cifratura con la prima chiave, una decifratura con la seconda ed infine un’altra cifratura con la terza chiave. Il TDES è sicuramente un algoritmo più complesso e più costoso rispetto al DES, specialmente in termini di burst time, ma è anche molto più sicuro, infatti non è stato ancora violato. - 10 - • • IDEA (International Data Encryption Algorithm): fu proposto da due scienziati svizzeri dello Swiss Federal Institute of Technology di Zurigo in collaborazione con la Ascom Communications inc.: il Dr. Xuejja. Lai e il Prof. James. Massey. IDEA rientra nella classe dei codici a blocchi, ogni blocco ha la dimensione di 64 bit e sfrutta chiavi di lunghezza di 128 bit. IDEA è generalmente considerato sicuro, poiché sfrutta chiavi di 128 bit. AES (Advanced Encryption Standard): fu sviluppato dai crittografi belgi Joan Daemen e Vincent Rijmen, è noto anche come algoritmo di Rijndael, nome derivato dal nome degli inventori. È stato adottato dalla National Institute of Standards and Technology (NIST) e dalla US Federal Information Processing Standard (FIPS) nel novembre 2001. L’AES rientra nella classe dei codici a blocchi, ogni blocco ha la dimensione di 128 bit e possono essere impiegate chiavi di 128, 192 o anche 256 bit. L’AES risulta essere un algoritmo molto robusto poiché permette l’impiego di chiavi di lunghezza fino a 256 bit. L’AES è di facile implementazione sia hardware che software e le sue richieste di risorse sono limitate, rendendolo utilizzabile su qualsiasi strumento capace di eseguire operazioni crittografiche. 1.3.2 La crittografia asimmetrica Nel 1976 Whitfield Diffie e Martin Hellmann, ricercatori all’università di Stanford USA, ipotizzarono un nuovo tipo di crittografia radicalmente nuovo. La loro idea era quella di utilizzare un cifrario asimmetrico, cioè un sistema in cui si sarebbero dovute usare due chiavi distinte, in cui una era utilizzata per la cifratura ed una per la decifratura ma la conoscenza di una chiave non permetteva la generazione dell’altra, nonostante fossero legate da una relazione matematica. Una delle due chiavi deve rimanere nota al solo possessore e per questo viene detta chiave privata. L’altra chiave invece può essere resa liberamente nota ed è utilizzabile da qualsiasi utente necessiti il suo impiego in una comunicazione, per questo viene detta chiave pubblica. Le due chiavi sono legate matematicamente tra loro, ma costruite in modo che sia impossibile risalire alla chiave privata partendo da quella pubblica. La crittografia asimmetrica incrementa notevolmente il grado di sicurezza rispetto a quello che si otterrebbe dall’impiego della crittografia simmetrica, in quanto viene eliminata l’esigenza di scambiare la chiave fra i due utenti. Nella crittografia asimmetrica un messaggio cifrato con una chiave appartenente alla coppia chiave privata, chiave pubblica, può essere decifrato solo con una chiave associata alla coppia. Per cifrare o decifrare un messaggio si può utilizzare indipendentemente una delle due chiavi della coppia, quindi l’utilizzo delle chiavi risulta essere completamente arbitrario ai fini della corretta applicazione dell’algoritmo. In base all’utilizzo di una o l’altra chiave in fase di cifratura o decifratura si possono dividere gli algoritmi in due categorie: - 11 - • algoritmi a chiave di cifratura pubblica: impiegano la chiave pubblica in cifratura e quella privata in decifratura; • algoritmi a chiave di decifratura pubblica: impiegano la chiave privata in cifratura e quella pubblica in decifratura. Una differenza importante con gli algoritmi a chiave simmetrica sta nel fatto che gli algoritmi a chiave pubblica possono essere implementati solo come algoritmi a blocchi, in quanto non sarebbe possibile cifrare i dati in ingresso bit a bit. 1.3.2.1 Algoritmi a chiave di cifratura pubblica Lo schema di principio di una comunicazione con crittografia a chiave di cifratura pubblica è il seguente: Figura 5. Schema di funzionamento di un algoritmo a chiave di cifratura pubblica Prima di analizzare l’effettiva trasmissione del messaggio bisogna analizzare la tipologia di chiavi di cui sono in possesso il mittente ed il destinatario. Il destinatario è in possesso di una coppia valida di chiavi: una chiave privata: K privD ed una chiave pubblica: K pubD . Il destinatario invierà a tutti i suoi interlocutori la propria chiave pubblica in modo che essi possano effettuare la cifratura di un messaggio. In questo modo anche il mittente avrà la chiave pubblica del destinatario. Il mittente cifra il messaggio utilizzando la chiave pubblica del destinatario, invia il messaggio attraverso il canale insicuro al destinatario il quale lo riceve e tramite l’utilizzo della propria chiave privata riesce a decifrarlo. - 12 - Il messaggio che passa attraverso il canale insicuro può essere letto da chiunque ma non avendo la chiave appartenente alla coppia: K privD , K pubD non potrà interpretarlo, quindi è garantito il servizio di segretezza. Il difetto fondamentale di questo tipo di algoritmo di cifratura asimmetrica consiste nel fatto che il destinatario non può essere sicuro dell’effettiva identità del mittente, in quanto chiunque può procurarsi la sua chiave pubblica e spedirgli dei messaggi cifrati. 1.3.2.2 Algoritmi a chiave di decifratura pubblica Lo schema di principio di una comunicazione con crittografia a chiave di decifratura pubblica è il seguente: Figura 6. Schema di funzionamento di un algoritmo a chiave di decifratura pubblica Prima di analizzare l’effettiva trasmissione del messaggio bisogna analizzare la tipologia di chiavi di cui sono in possesso il mittente ed il destinatario. Il mittente è in possesso di una coppia valida di chiavi: una chiave privata: K privM ed una chiave pubblica: K pubM . Il mittente invierà a tutti i suoi interlocutori la propria chiave pubblica in modo che essi possano effettuare la cifratura di un messaggio. In questo modo anche il destinatario avrà la chiave pubblica del mittente. Il mittente cifra il messaggio utilizzando la propria chiave privata, invia il messaggio attraverso il canale insicuro al destinatario il quale lo riceve e tramite l’utilizzo della chiave pubblica del mittente riesce a decifrarlo. - 13 - Il messaggio che passa attraverso il canale insicuro può essere letto ed interpretato da chiunque poiché chiunque può avere la chiave pubblica del mittente, quindi in questo caso il servizio di segretezza non è garantito. Il vantaggio fondamentale di questo tipo di algoritmo di cifratura asimmetrica si rileva nel fatto che il destinatario se riesce a decifrare il messaggio con la chiave pubblica del mittente è sicuro che il messaggio è stato cifrato da quest’ultimo, quindi è garantito il servizio di autenticazione d’identità. Gli algoritmi a chiave di decifratura pubblica trovano largo impiego nei sistemi che fanno uso della firma digitale. 1.3.2.3 Algoritmi ibridi Si è visto che gli algoritmi a chiave di cifratura pubblica garantiscono il servizio di segretezza ma non quello di autenticazione di identità mentre gli algoritmi a chiave di decifratura pubblica garantiscono gli stessi servizi in modo opposto. Sarebbe molto proficuo utilizzare contemporaneamente una combinazione dei due algoritmi per ottenere entrambi i servizi che costituiscono delle caratteristiche estremamente importanti ai fini della sicurezza. Lo schema di principio dell’utilizzo combinato delle due tecniche è il seguente: Figura 7. Schema di utilizzo della cifratura asimmetrica per l’invio di un messaggio autenticato e segreto Appurato il fatto che il mittente sia in possesso della sua chiave privata: K privM , della sua chiave pubblica: K pubM e della chiave pubblica del destinatario: K pubD e che il destinatario è in possesso della sua chiave privata: K privD , della sua chiave - 14 - pubblica: K pubD e della chiave pubblica del mittente: K pubM , è possibile analizzare l’effettiva trasmissione del messaggio. Il mittente cifra il messaggio con la propria chiave privata ottenendo un messaggio cifrato intermedio, dopodichè cifrerà il medesimo con la chiave pubblica del destinatario, ottenendo un messaggio cifrato finale diverso da quello intermedio. Ora il mittente invia tramite il canale insicuro il messaggio cifrato che giungerà al mittente. Il mittente per ottenere il messaggio in chiaro dovrà decifrarlo prima con la sua chiave privata e poi con quella pubblica del mittente. In realtà, in cifratura, l’ordine di utilizzo delle due chiavi è arbitrario, ma deve essere noto, in quanto chi riceve il messaggio dovrà eseguire la decifratura impiegando le chiavi in ordine inverso. Il servizio di autenticazione del messaggio è garantito dall’utilizzo della chiave privata del mittente mentre il servizio di segretezza è garantito dall’impiego della chiave privata del destinatario. Un vantaggio che si ha rispetto alla crittografia simmetrica sta nel numero di chiavi che gli utenti devono gestire. Nella crittografia asimmetrica ogni utente dispone di una propria coppia di chiavi ed utilizza sempre questa per comunicare con qualsiasi altro utente. In questo modo si ha un incremento lineare del numero di chiavi in funzione del numero di utenti. Il modello presentato risulta essere molto complicato da applicare in casi reali, quindi si opta per l’utilizzo di tecniche miste che uniscono la semplicità della crittografia simmetrica con la sicurezza di quella asimmetrica. Un esempio eccellente riguarda la tecnica impiegata nel protocollo SSL in cui, nella comunicazione tra due entità, viene creata “al volo” una chiave segreta, detta chiave di sessione, per cifrare il messaggio. La chiave a sua volta viene cifrata con la chiave pubblica del destinatario ed allegata al messaggio stesso. 1.3.2.4 I principali algoritmi a chiave asimmetrica I principali algoritmi a chiave asimmetrica sono: • RSA (Rivest Shamir Adelman): fu sviluppato da Ron Rivest, Adi Shamir e Len Adelman, nel 1977 e poi brevettato nel 1983 negli Stati Uniti dal MIT. Si basa sul problema matematico della fattorizzazione di un numero, che consiste nel trovare quei numeri primi che, moltiplicati tra loro, forniscono il numero dato. Questo è un problema tanto più complesso quanto più è grande il numero iniziale. Basti pensare che una possibile soluzione al problema sarebbe nel verificare l’ipotesi di Riemann che, a tutt’oggi, è uno dei sette problemi del millennio per cui l’istituto matematico Clay offre un milione di dollari a chi riesca a risolverlo. L'RSA è solitamente combinato insieme ad un algoritmo a chiave privata. Combinandolo ad esempio con il DES l'RSA fornisce dimensioni di chiavi sino a 2048 bit. L'RSA è considerato sicuro se sono usate chiavi - 15 - • abbastanza lunghe in quanto la sua sicurezza si basa sulla difficoltà di fattorizzare numeri interi molto grandi. DSA (Digital Signature Algorithm): è stato proposto nel 1991 dal NIST per l’utilizzo nell’ambito dello standard DSS (Digital Signature Standard), è stato progettato appositamente per la realizzazione di firme digitali. Il procedimento di cifratura infatti prevede anche l’utilizzo di una funzione di Hash (nello specifico la funzione SHA) ed apposite operazioni di verifica della firma. Esso si basa sulla difficoltà di risoluzione del problema del logaritmo discreto. Il logaritmo discreto è definito come il logaritmo, in una certa base n , su un gruppo algebrico (a differenza del logaritmo tradizionale che è definito sull'insieme dei numeri reali). Ad esempio, si consideri il gruppo moltiplicativo degli interi modulo p : G = Z *p con p numero primo. Si consideri poi l'equazione: a x = b mod p dove l’operatore “mod” è il resto della divisione b p . Il problema del logaritmo discreto consiste nel trovare il valore di x tale che l'equazione è soddisfatta. Non sono noti ad oggi algoritmi efficienti per il calcolo dei logaritmi discreti. Si tratta in pratica di un problema NP (non-polinomiale) la cui soluzione richiede un tempo di calcolo esponenziale rispetto alle dimensione del gruppo algebrico G . È proprio l'appartenenza alla classe dei problemi NP che pone il problema del logaritmo discreto alla base nella realizzazione di sistemi crittografici a chiave pubblica. In linea teorica sarebbe possibile ricavare la chiave privata calcolando il logaritmo discreto di quella pubblica. Nella realtà però questa operazione è impraticabile a causa della grandezza dei numeri in gioco. Tale algoritmo però non viene molto impiegato in quanto esistono algoritmi ritenuti molto più sicuri. 1.3.3 Stima sulla sicurezza degli algoritmi crittografici Agli albori della crittografia, un algoritmo veniva valutato sicuro seguendo il principio di “security by obscurity”, secondo il quale esso risultava sicuro fintantoché rimaneva segreto a chi non era autorizzato ad accedervi, in quanto la mancata conoscenza non permetteva che questo potesse essere violato. Questa metodologia era radicata profondamente in quanto gli algoritmi erano poco elaborati e quindi era facile violarli. La crittografia moderna non si basa più su questa idea ma sul postulato enunciato da August Kerckhoffs nel 1883 per cui: “la sicurezza di un sistema crittografico è basata esclusivamente sulla conoscenza della chiave”. I maggiori algoritmi utilizzati per la crittografia sono di dominio pubblico e quindi chiunque ne conosce la metodologia di lavoro ma nonostante ciò questo non costituisce una minaccia alla sicurezza dell’algoritmo stesso ma anzi, al contrario, - 16 - è un incentivo al loro sviluppo, in quanto è più facile scoprirne difetti e debolezze e quindi porvi rimedio. La sicurezza di un algoritmo crittografico dipende dalle particolari proprietà matematiche della classe dei problemi NP, la cui soluzione necessita di una quantità di tempo che aumenta esponenzialmente con i dati in ingresso, rendendo gli attacchi a questi sistemi tanto più impraticabili quanto più aumenta la lunghezza delle chiavi coinvolte nei processi di cifratura e decifratura. La chiave non è altro che una sequenza di bit generata in modo più o meno casuale che, in linea teorica, può sempre essere ricavata. Esistono infatti dei metodi di ricerca esaustiva che consentono di risalire alla chiave utilizzata nella cifratura provando ad applicare l’algoritmo ad un testo cifrato, impiegando chiavi sempre diverse, fino a che non si ottiene un risultato sensato. Questo modo di procedere è noto come “attacco di forza bruta” (brute force attack). Il successo di questo tipo di attacchi dipende dal numero massimo di tentativi che possono essere fatti, ovvero dal numero massimo di chiavi che quel particolare algoritmo può accettare, cioè dal keyspace. La tabella sotto mostra quanto influisce la lunghezza della chiave in un attacco a forza bruta sui tempi necessari a trovare la chiave giusta ipotizzando una forza di calcolo pari a un milione di tentativi al secondo. Lunghezza della chiave 32 bit 40 bit 48 bit 56 bit 64 bit Dimensioni del keyspace 4,29 · 109 1,1 · 1012 2,81 · 1014 7,21 · 1016 1,84 · 1019 Tempo di ricerca 1,2 ore 13 giorni 8,9 anni 2.300 anni 580.000 anni Tabella 1. Tempo di ricerca necessario per un attacco di forza bruta nell’ipotesi che si possano effettuare un milione di tentativi al secondo Si ribadisce che, per quanto riguarda la sicurezza del sistema, la chiave gioca un ruolo fondamentale ed in particolare in relazione alla sua lunghezza. Si potrebbe pensare, in via teorica di creare una chiave estremamente lunga, in grado di vanificare ogni attacco di forza bruta, ma ciò avrebbe come svantaggio un tempo notevolmente grande per implementare l’algoritmo di cifratura e decifratura del messaggio. Dunque ci si trova di fronte ad un problema di ottimizzazione fra il vantaggio della sicurezza che induce una chiave moto lunga e lo svantaggio di notevoli tempi che rallenterebbero la fase di cifratura e decifratura. Per risolvere questo problema si va a valutare la tipologia di dati che si sta tentando di proteggere: il livello di sicurezza del sistema deve essere tanto più alto quanto più sono sensibili le informazioni in gioco. Secondo quanto detto, è opportuno tener presente il costo richiesto per violare l’algoritmo che deve essere superiore al valore dei dati protetti ed il tempo richiesto per violare l’algoritmo che deve risultare superiore al tempo per cui i dati cifrati devono rimanere segreti. - 17 - Tenendo da conto quanto appena esposto, qualsiasi tentativo di attacco, anche se andasse a buon fine, non comporterebbe alcun vantaggio a colui che appronta il tentativo d’intrusione. 1.3.4 La funzione di Hash La funzione di Hash è un processo di trasformazione dei bit, che si differenzia dai normali algoritmi crittografici, in quanto dopo la cifratura del messaggio non è possibile effettuare la decifratura. Quando si applica un messaggio ad una funzione di Hash essa restituisce una stringa di bit di lunghezza fissa, che rappresenta una sorta di sintesi matematica del messaggio, detta “message digest” (impronta digitale). La funzione di Hash gode di alcune proprietà che la rendono crittograficamente sicura: • è applicabile a qualsiasi sequenza di dati di qualsiasi tipo e dimensione; • il risultato della sua applicazione è una stringa di bit di dimensione fissa indipendente dalla dimensione del messaggio originale; • la variazione di un solo bit del messaggio originale comporta la variazione dell’intero digest; • è una funzione a senso unico (one way), cioè risulta impossibile risalire al messaggio originale partendo dal digest; • è una funzione senza collisioni (collision free), nel senso che è impossibile trovare due messaggi diversi tra loro che restituiscano lo stesso digest. In realtà la funzione di Hash è una applicazione non iniettiva caratterizzata da un dominio con uno spazio di dimensione infinita che rappresenta l’insieme di tutti i messaggi in chiaro di qualsiasi lunghezza e un codominio con uno spazio a dimensione finita che rappresenta l’insieme di tutti i possibili digest di lunghezza prefissata. Risulta però matematicamente impossibile che ad ogni elemento del dominio corrisponda un unico elemento del codominio, è comunque talmente bassa la probabilità che a due elementi distinti del dominio corrisponda uno stesso elemento del codominio che si può dire che la funzione di Hash è una relazione senza collisioni ed a senso unico. Si può dunque dire che due messaggi che hanno lo stesso digest sono uguali tra loro e questo ci permette di utilizzare la funzione di Hash ogni qualvolta il fine è quello di confrontare due messaggi tra loro per verificarne l’uguaglianza. Essendo i messaggi di dimensioni arbitrarie, risulta sicuramente più comodo eseguire il confronto direttamente sui digest che sono a lunghezza fissa. Questa tecnica apre la strada ad una delle più importanti applicazioni della funzione di Hash: la firma digitale. - 18 - 1.3.4.1 I principali algoritmi per la funzione di Hash I principali algoritmi che la funzione di Hash utilizza sono: • MD5 (Message Digest 5): è un algoritmo sviluppato dai laboratori RSA che fornisce digest di 128 bit, nel 2006 si sono presentati casi di collisioni; • SHA (Secure Hash Algorithm): chiamato anche Secure Hash Standard (SHS) è un algoritmo pubblicato dal Governo degli Stati Uniti che produce digest di 160 bit; • SHA-1: è la versione migliorata del precedente algoritmo. Produce anch’esso digest di 160 bit, ma, a differenza del suo predecessore, l’SHA1 non è stato ancora violato; • SHA-256, SHA-512, SHA-384: sono stati sviluppati e proposti nel Draft Federal Information Processing Standard (FIPS) nel 2001; rispettivamente producono un digest di 256 bit, 512 bit e 384 bit aumentando quindi la sicurezza rispetto a SHA-1. 1.3.4.2 Stima sulla sicurezza della funzione di Hash La funzione di Hash è un algoritmo di cifratura one-way che codifica secondo funzioni matematiche non invertibili un messaggio, cioè non è possibile decodificare il digest a partire dal risultato cifrato. Il digest è teoricamente univoco, non dovrebbero quindi esistere due stringhe diverse che codificate generino lo stesso risultato cifrato, tale eventualità è chiamata collisione e una sua scoperta porterà lentamente l’algoritmo ad essere dichiarato non sicuro. Le combinazioni possibili, variano da algoritmo a algoritmo, sono superiori all’ordine di milioni di miliardi, lasciando l’ingannevole fiducia sull’indecifrabilità del digest. Negli anni ottanta il matematico americano Martin Hellman studiò una tecnica chiamata “time memory trade-off” (compromesso tempo-memoria) per decifrare il digest generato da una funzione di Hash. In pratica Hellman propose di creare dei dizionari in cui venivano inseriti tutti i digest generati da ogni possibile messaggio. La conseguenza è la creazione di un grande vantaggio nella ricerca del digest, infatti in un attacco di forza bruta, il maggiore spreco di burst time sta nella codifica ripetuta della generazione del digest e non nel confrontarlo, mentre con l’uso del dizionario si va ad effettuare direttamente il confronto. Il problema di questa tecnica sta nella dimensione del dizionario infatti un dizionario capace di contenere tutti i digest frutto delle combinazioni alfanumeriche di messaggi sufficientemente lunghi supererebbe le decine di terabyte. Per ovviare a ciò Philippe Oechsin, un esperto in sicurezza, ha ideato un procedimento per ridurre il dizionario di digest in una tabella molto più piccola chiamata “rainbow tables”. - 19 - Come prima cosa si codificano i messaggi e si ottengono i digest corrispondenti; ora viene definita una formula di riduzione R ( hash ) tale che ad un digest faccia corrispondere un messaggio: ovviamente non sarà quello di partenza, poiché è impossibile invertire una funzione di Hash. Si itera t volte il seguente procedimento: si genera l’hash del nuovo messaggio e si applica la formula di riduzione Ri ( hashi ) con i = 1, 2,… , t dove i indica i passaggi effettuati. Si verrà quindi a definire che: f i +1 = Ri ( Hash Algorithm ( f i ) ) dove f i = messaggioi , ovvero il messaggio al passo i + 1 è il risultato della formula di riduzione i -esima applicata all’ i -esimo digest. In questo modo si genererà una catena “password-hash-password-hash” di migliaia di elementi ma di questi solo il primo e l’ultimo verranno conservati su disco con un risparmio di memoria pari a t volte. Ora quando si vorrà confrontare un digest si applicherà f t e si controllerà che il risultato non sia l’ultimo elemento di una delle catene (di cui come appena detto conserviamo solo l’inizio e la fine); se ciò non fosse vero si applicherà in sequenza ft −1 e f t e si riconfronterà il risultato con l’ultimo elemento delle catene. Continuando con questo procedimento all’ i -esimo passaggio si ricaverà che il digest ricercato fa parte di una certa catena X e quindi con discreta probabilità il messaggio che l’ha generato sarà quello che precede nella catena. Non è possibile risalire la catena (si dovrebbe invertire una funzione di Hash) ma è stato memorizzato l’inizio di tale catena e si può perciò riapplicargli in sequenza gli t − i passi necessari a ritrovare il messaggio del passo precedente. Si ricava il digest del messaggio e lo si confronta con il risultato, scoprendo che è quello giusto. Questo metodo porta raramente a falsi allarmi dovuti a eventuali collisioni, generati dalle formule di riduzione lungo la catena; al contrario la versione originale di tale metodo applicava un’unica R ( hashi ) ad ogni passo, con rilevanti percentuali di errore. Tale procedimento è velocissimo ed il tempo di cracking si riduce col quadrato della grandezza della tabella, cioè incredibilmente, usare una tabella da 1 gigabyte è 16 volte più veloce di una da 256 megabyte. Questo metodo risulta molto efficace quando i messaggi sono relativamente corti e quindi ottimale nel violare le password. 1.3.5 La firma digitale In quasi tutte le attività lavorative risulta indispensabile per verificare l’autenticità di un atto l’apposizione di una firma autografa. In ambito informatico questo è possibile per mezzo della firma digitale. - 20 - L’implementazione e la validazione di una firma digitale richiedono l’utilizzo di una funzione di Hash e di un algoritmo di crittografia asimmetrica. Lo schema di principio con cui si realizza la firma digitale di un documento informatico è il seguente: Figura 8. Processo di firma digitale di un documento informatico Il messaggio originale viene elaborato mediante una funzione di Hash generando così il suo digest, a questo punto il digest viene cifrato utilizzando la chiave privata del mittente K priv M , infine si costituisce un documento finale costituito dal messaggio originale, dalla firma ed eventualmente dal certificato contenente la chiave pubblica di decifratura. Il documento così ottenuto viene trasmesso al destinatario, che dovrà verificarne l’autenticità al fine di garantire la correttezza della trasmissione. Lo schema di principio con cui si verifica l’autenticità della firma digitale di un documento informatico è il seguente: Figura 9. Processo di verifica della firma digitale Il documento firmato viene ripartito in due parti, in una è contenuta il messaggio originale e nell’altra è contenuta la firma. Il messaggio in chiaro viene elaborato mediante una funzione di Hash generando così il suo corrispondente digest. - 21 - La firma viene decifrata con la chiave pubblica del mittente K pub M , recuperata dal certificato relativo al mittente stesso, ottenendo così il “digest presunto”. A questo punto si effettua il confronto fra il digest ottenuto ed il “digest presunto”. Questa operazione può portare a due situazioni: i digest sono uguali, allora la firma è valida ed il documento è integro oppure i digest sono diversi e questo segnala che il documento ha subito delle modifiche che potrebbero essere di natura dolosa o accidentale, dal momento in cui è stato firmato dal mittente a quello in cui è stato ricevuto dal destinatario. La firma digitale così ottenuta lega l’entità, che detiene l’uso esclusivo della chiave privata, ai dati, tramite l’impiego del digest, infatti ogni firma, anche se apposta con la stesa chiave privata, è unica e diversa in relazione ad un diverso documento. Si può, inoltre, precisare che il documento e la sua firma non devono necessariamente essere trasmessi sullo stesso canale, infatti un invio separato non toglie il legame esistente fra di esse. La firma digitale offre tutte le garanzie di sicurezza di una normale firma autografa, apportando una maggiore difficoltà alla falsificazione del documento, in quanto si dovrebbe entrare in possesso della chiave privata del mittente, e garantisce l’integrità del documento, in quanto una qualsiasi modifica del documento implicherebbe la variazione dell’intero digest. La fase di trasmissione avviene su canali sicuri, come ad esempio l’SSL, garantendo così la sicurezza anche da attacchi del tipo “man in the middle”. - 22 - 1.4 La public key infrastructure Precedentemente si è discusso sulla validità degli algoritmi crittografici e sul vantaggio di utilizzare la firma digitale al fine di garantire la sicurezza e la segretezza della trasmissione di un messaggio. Il problema non è stato completamente risolto in quanto, ipotizzato un corretto utilizzo da parte dell’utente delle metodologie prima elencate, per esempio la creazione ed il mantenimento di riservatezza della chiave, la firma digitale lega un documento non al suo autore, ma alla chiave che esso ha utilizzato nell’operazione di cifratura, si necessita quindi di un’entità che assicuri che quella chiave appartenga effettivamente all’autore del messaggio. Inoltre si deve garantire che lo scambio e la divulgazione delle chiavi pubbliche avvengano in modo sicuro. Per questo si deve introdurre il concetto di “infrastruttura a chiave pubblica”, in cui un’autorità fidata si preoccupa di identificare e autenticare tutti gli utenti che vogliano usufruire dei suoi servizi garantendo, inoltre l’appartenenza di una chiave pubblica ad uno specifico utente mediante l’emanazione di un certificato. L’ infrastruttura a chiave pubblica, definita acronicamente come “PKI”, può essere definita in vari modi, per esempio il NIST, nel documento “Introduction to Pulic Key Technology and the Federal PKI Infastructure” del 2001, la definisce come “una gerarchia monopolizzata usata per l’amministrazione di certificati e delle chiavi pubbliche”, oppure può essere definita come una infrastruttura che fornisce servizi di cifratura e firma digitale ad utenti. Inglobando le varie definizioni in una unica, si può dire che una PKI risulta essere un insieme di hardware, software, risorse umane, protocolli, regole e procedure necessarie per rendere possibile l’utilizzo su larga scala dei sistemi di crittografia asimmetrici. 1.4.1 Il certificato a chiave pubblica Un certificato a chiave pubblica è un documento informatico che associa una chiave pubblica ad un soggetto, per essere valido ai fini di legge deve essere firmato digitalmente da una entità preposta, detta autorità di certificazione che autentica l’effettiva appartenenza della chiave al soggetto. Un certificato a chiave pubblica deve quindi contenere: • i dati anagrafici del soggetto titolare, denominato subject; • la chiave pubblica del soggetto ed informazioni relative allo stesso come l’algoritmo di cifratura utilizzato, impieghi previsti, etc; • il periodo di validità del certificato; • i dati identificativi dell’emettitore del certificato, denominato issuer; • un digest delle informazioni precedenti cifrato con la chiave privata dell’emettitore, denominato signature. - 23 - Gli schemi sotto mostrano i processi di firma e verifica di un certificato: Figura 10. Firma del certificato da parte di un soggetto e di una autorità di certificazione. Figura 11. Verifica di un certificato a chiave pubblica. Quando il mittente vuole stabilire una comunicazione con il destinatario richiede ad una autorità di certificazione di farsi rilasciare un certificato a chiave pubblica. A questo punto il mittente appone la firma sul documento originale ed invia al destinatario sia il documento firmato sia il certificato. Il destinatario all’arrivo del messaggio, verifica l’attendibilità del certificato e l’autenticità del documento firmato. Per fare ciò il destinatario decifra la signature con la chiave pubblica della dell’issuer ottenendo un digest presunto da confrontare con il digest ricavato da tutti gli altri campi del certificato elaborati con la funzione di Hash. Se il digest presunto è uguale al digest ricavato allora il - 24 - certificato è valido e si procede alla verifica della firma, nel caso siano diversi, ciò implica che il certificato non è originale. L’utilizzo dei certificati a chiave pubblica è necessario solo se si vuole conseguire la validità ai fine di legge, quindi è del tutto lecito scambiare documenti con la tecnica delle chiavi asimmetriche senza ricorrere all’uso dei certificati, se ad esempio i documenti da scambiare non hanno una grande riservatezza. 1.4.1.1 Richiesta, ottenimento, durata e revoca di un certificato Un utente che desidera richiedere un certificato ad una autorità di certificazione deve come prima cosa registrarsi presso una autorità di registrazione. La procedura di registrazione costituisce una prima importante relazione tra l’utente e l’autorità di certificazione. Una volta attestata l'autenticità dell’identità dell’utente, l’autorità di registrazione lo registra in un suo dominio di fiducia. Il fine primario della registrazione è quindi quello di garantire che la chiave pubblica richiesta da un utente sia effettivamente associata e quindi appartenga solo a quel determinato utente. Terminata la fase di registrazione, l'utente può richiedere l'emissione di un certificato. Durante la procedura di generazione di un certificato l'utente sottopone all'autorità di certificazione le informazioni da certificare e quest'ultima verifica l'accuratezza delle stesse in accordo a politiche e standard applicabili. L'autorità di certificazione firma poi le informazioni, generando il certificato e lo pubblica sul sistema scelto per la distribuzione dei certificati. L’autorità di certificazione può anche archiviare una copia del certificato e registrare ogni operazione di generazione di certificati su di un proprio archivio. La richiesta di un certificato è realizzata di solito in formato PEM. Il formato PEM prevede l’utilizzo di due Encapsulation Block, che indicano la posizione di inizio e di fine del certificato memorizzato nel file. All’interno di ogni Encapsulation Block viene utilizzata la codifica Base64. Figura 12. Frammento di file contenente una richiesta di certificato L'utente genera sul proprio computer una coppia di chiavi utilizzando il software fornito dall’autorità di certificazione oppure utilizzando quello già incluso nei browser. La chiave privata viene memorizzata localmente in un file nascosto. Se è richiesta una maggiore sicurezza la coppia di chiavi può venire generata tramite una Smart - 25 - Card collegata ad un computer e la chiave privata resta sempre memorizzata solo sulla Smart Card protetta quindi da PIN. Tramite posta elettronica l'utente invia una richiesta di certificato unitamente alla chiave pubblica generata, all’autorità di certificazione di sua fiducia. L’autorità di certificazione verifica l'identità del richiedente, di solito è prevista la richiesta di presentarsi di persona presso l’autorità di certificazione. Effettuato poi il controllo, l’autorità di certificazione emette il certificato come documento elettronico, lo invia al richiedente tramite posta elettronica e lo inserisce nel registro delle chiavi pubbliche. Ogni certificato generato ha una validità temporale limitata al cui termine va sostituito. Il periodo di validità di un certificato, in assenza di compromissioni o di usi illeciti, garantisce all'utente che deve utilizzare tale certificato, che la chiave pubblica può essere utilizzata per lo scopo per cui è stata generata e che l'associazione tra la chiave pubblica e le altre informazioni contenute nel certificato è ancora valida. L’autorità di certificazione deve poter garantire misure efficaci contro usi fraudolenti ed illeciti di un certificato; lo strumento che può utilizzare è la revoca. Un certificato deve essere revocato in presenza delle seguenti condizioni: • compromissione rilevata o semplicemente sospettata della chiave privata corrispondente alla chiave pubblica contenuta nel certificato; • cambiamento di una qualsiasi delle informazioni contenute nel certificato o delle condizioni iniziali di registrazione. Un'autorità di certificazione che necessita di revocare dei certificati prima della loro scadenza naturale deve pubblicare un elenco degli stessi nel “Certificate Revocation List” (CRL), in modo che tutti gli utenti appartenenti alla stessa infrastruttura siano informati delle avvenute revoche. Le liste di revoca giocano un ruolo di fondamentale importanza in tutti i processi crittografici, in quanto devono essere consultate sia in fase di cifratura che in fase di verifica di una firma, poichè prima di cifrare un messaggio, va verificato che la chiave pubblica che si deve usare appartenga ad un certificato valido. Analogo discorso vale per la verifica di una firma, che può essere ritenuta valida solo se il certificato del firmatario, oltre ad essere autentico ed integro, non è stato revocato. L'autenticità e l' integrità del certificato si controlla verificando la firma dell’autorità di certificazione che ha emesso il certificato. Lo stato di revoca si controlla verificando che il certificato in questione non compaia nella lista di revoca emessa dall’autorità di certificazione competente. Il metodo di distribuzione della notifica di revoca attraverso CRL, distribuite periodicamente dall'autorità di certificazione, è detto di tipo “pull”, in quanto sono gli stessi utenti finali a fornirsi delle CRL dal sistema di distribuzione quando è necessario. Il limite legato al metodo di distribuzione delle notifiche delle revoche tramite CRL, è quello della latenza introdotta, infatti quest’ultima dipende dalla periodicità di emissione delle CRL, che deriva dalle politiche di sicurezza stabilite dall'organizzazione. Una determinata politica potrebbe richiedere la pubblicazione - 26 - di una nuova CRL ogni qualvolta si richiede una revoca ma tale procedura risulta, tuttavia, molto dispendiosa dal punto di vista amministrativo. Per ovviare a questo problema, lo standard X.509 introduce una tipologia particolare di CRL definita “Delta CRL”. Con cadenza stabilita viene pubblicata una CRL base a cui viene associata una Delta CRL che contiene gli aggiornamenti alla CRL base. Date le dimensioni ridotte, le Delta CRL possono essere pubblicate con maggiore frequenza senza comportare un eccessivo consumo di risorse di rete. 1.4.2 Autorità di certificazione Come descritto nel paragrafo precedente esistono due entità che si occupano della sicurezza dei certificati: l’autorità di registrazione e l’autorità di certificazione. L’autorità di registrazione si occupa di verificare l’effettività identità dell’utente richiedente in quanto il certificato deve appartenere ad un solo utente: il suo possessore. Una volta che l’autorità di registrazione ha attestato la validità dell'identità dell'utente attraverso una serie di procedure definite da una determinata politica di sicurezza, ha il compito di abilitare l'utente come appartenente ad uno specifico dominio di fiducia. Spesso la funzionalità di autorità di registrazione viene espletata dall'autorità di certificazione. Una volta che l’autorità di registrazione ha abilitato l’utente come appartenente ad uno specifico dominio di fiducia l’autorità di certificazione crea un certificato per quell’utente. L’attività di un’autorità di certificazione non si limita esclusivamente alla generazione dei certificati, ma deve poterne gestire l'intero ciclo di vita, deve quindi occuparsi anche delle fasi successive quali l’aggiornamento e la revoca. Un ulteriore compito dell'autorità di certificazione è stabilire relazioni di fiducia con le altre autorità di certificazione. Un'autorità di certificazione può operare in modo autonomo, oppure appartenere ad una struttura più articolata. Le autorità di certificazione possono essere collegate essenzialmente in due modi: • modello reticolare; • modello ad albero. Nel modello reticolare ogni autorità di certificazione nasce come autorità locale di cui si fidano tutti e solo gli utenti appartenenti ad essa, e le altre autorità di certificazione comunicano tra loro la veridicità di un certificato attraverso un certificato particolare detto “cross certificate”. Lo svantaggio di un tale modello sta nel fatto che le autorità di certificazione potrebbero adottare politiche di sicurezza diverse e quindi il passaggio di un cross certificate da un’autorità che garantisce una sicurezza maggiore ad una che ne garantisce di inferiori produrrà un decadimento sulla sicurezza del certificato. - 27 - Nel modello ad albero è prevista un’autorità di certificazione globale, la quale garantisce per se stessa autocertificandosi, che demanda ed organizza il compito di certificazione a strutture inferiori, firmando il loro certificato con la propria chiave privata e le autorità inferiori possono avere a loro volta la responsabilità sulla certificazione di altre autorità di livello inferiore. Lo svantaggio sulla riduzione della sicurezza che si verifica nel modello reticolare, non si presenta in questo modello, in quanto è l’autorità globale a dettare le politiche di sicurezza per tutte le altre autorità facenti parte alla struttura. Pensando ad una rete globale, è facile immaginare che esistano una molteplicità di autorità di certificazione, con diverse politiche di sicurezza, che devono operare fra loro, è quindi impensabile che ogni utente abbia diretta conoscenza delle chiavi pubbliche di ogni potenziale interlocutore, o delle chiavi pubbliche delle corrispondenti autorità di certificazione. Per risolvere questo problema si utilizzano le cosiddette catene di certificazione, in cui l'utente che riceve un messaggio firmato verifica prima di tutto l'autenticità del certificato con la chiave pubblica dell’autorità di certificazione che lo ha emesso e poi la firma del mittente con la sua chiave pubblica contenuta nel certificato. In questo modo è possibile allegare ad un messaggio che si vuole autenticare più certificati, definendo una vera e propria gerarchia di certificati, in cui ognuno attesta l'autenticità del precedente. 1.4.3 Standard X.509 Lo standard X.509 definisce il formato per i certificati a chiave pubblica e ne regola l’esistenza. Lo standard X.509 è descritto nelle specifiche ITU-T corrispondenti alle specifiche ISO/IEC 9594-8. La prima versione dello standard X.509 fu presentata nel 1998, prevedeva un sistema gerarchico delle autorità di certificazione per l’emissione dei certificati, in cui solo l’autorità di certificazione poteva validare un certificato, in contrapposizione ai modelli allora esistenti di fiducia in cui chiunque poteva firmare e quindi attestare la validità delle chiavi altrui. Trascorsi degli anni la versione 1 risultò inadeguata e vennero introdotte la seconda e la terza versione in cui vennero aggiunti dei nuovi controlli di accesso dei campi, di sostegno e dell’indice e furono integrate la compatibilità con altre topologie, aumentando la flessibilità; ad esempio fu permesso l’utilizzo in reti di fiducia peer to peer. - 28 - La struttura di un certificato X.509 v3 è mostrata in figura: Figura 13. Struttura di un certificato X.509 v3 • • • • • • • • • • Version: indica la versione del formato del certificato. Serial Number: è un codice numerico che identifica univocamente il certificato. Signature Algorithm: specifica l’algoritmo utilizzato dall’autorità di certificazione per firmare il certificato. Issuer X.500 Name: indica il nome dell’autorità di certificazione secondo lo standard di naming X.500. Validity Period: specifica la data e l’ora di inizio validità e fine validità del certificato. Subject X.500 Name: nome del possessore del certificato. Subject Public Key Information: contiene il valore della chiave pubblica del possessore del certificato e l’algoritmo con cui tale chiave viene usata. Issuer Unique Identifier: è una stringa di bit aggiuntivi, opzionale, usata nel caso in cui nella struttura ad albero ci siano due autorità di certificazione. Subject Unique Identifier: stringa opzionale di bit usata nel caso di omonimia tra due membri di una stessa autorità di certificazione. Extensions: è suddiviso in tre sottocampi: l’identificatore del tipo di estensione, un indicatore di criticità e il valore effettivo dell’estensione. L’indicatore di criticità facilita l’interoperabilità tra sistemi che non utilizzano certificati con determinate estensioni e sistemi che, invece interpretano tutte le estensioni definite a livello di standard. Tale indicatore è un flag che indica se l’estensione è critica o non critica; nell’ultimo caso il sistema che deve elaborare il certificato può - 29 - • eventualmente ignorare l’estensione in questione se non è in grado di interpretarla. CA Signature: è la firma digitale dell’autorità di certificazione sul certificato. 1.4.4 Standard PKCS PKCS (Public Key Cryptography Standard) è uno standard rilasciato dalla RSA Laboratories nel 1991 che permette di incrementare lo sviluppo e l’impiego delle tecniche di crittografia asimmetrica. Il PKCS nel corso del tempo è diventato lo standard per questo tipo di tecnologie ed è usato in molte applicazioni come ad esempio nel protocollo S/MIME utilizzato per la posta elettronica e nel protocollo SSL impiegato per fornire sicurezza nelle comunicazioni su Internet. Il PKCS è costituito da quindici normative che definiscono gli algoritmi di crittografia, la sintassi e le interfacce per l’utilizzo dei vari strumenti crittografici: • PKCS#1: RSA Cryptography Standard. Definisce le linee guida per l’implementazione dell’algoritmo RSA sui calcolatori digitali. Lo standard PKCS#1 incorpora PKCS#2 e PKCS#4. • PKCS#3: Diffie-Hellman Key Agreement Standard. Descrive un metodo per l’implementazione dell’algoritmo di Diffie-Hellman. • PKCS#5: Password-Based Criptography Standard. Prescrive delle raccomandazioni per l’implementazione della cifratura basata su password. • PKCS#6: Extended-Certificate Sysntax Standard. Propone una sintassi ASN.1 che estende quella dei certificati X.509. • PKCS#7: Cryptographic Message Syntax Standard. Descrive la sintassi generale per i dati crittografati. Su questo formato si basa ad esempio il protocollo di posta elettronica S/MIME e molti altri formati di interscambio per le informazioni di identificazione personale. • PKCS#8: Private-Key Information Syntax Standard. Descrive la sintassi per le informazioni relative ad una chiave privata, e per le chiavi private cifrate. • PKCS#9: Selected Attribute Types. Definisce i tipi ASN.1 degli attributi usati in PKCS#6, PKCS#7, PKCS#8 e PKCS#10. • PKCS#10: Certification Request Syntax Standard. Descrive una sintassi per la richiesta di firma di un certificato. • PKCS#11: Cryptographic Token Interface Standard. Specifica una API (Cryptoki) per i dispositivi in grado di eseguire operazioni crittografiche e trattare le informazioni relative (chiavi e certificati). È lo standard de facto per l’interoperabilità tra i diversi modelli e tipi di HSM (Hardware Security Module). Definisce un modello ad oggetti del token indipendente dall’hardware sottostante e dalla piattaforma operativa. - 30 - • • • PKCS#12: Personal Information Excange Syntax Standard. Specifica un formato per la memorizzazione o il trasporto di informazioni personali, come chiavi private, certificati e altro. È il formato utilizzato nei browser più comuni per l’importazione e l’esportazione di chiavi e certificati. PKCS#13: Elliptic Curve Cryptografy Standard. Specifica molti aspetti della crittografia basata sulla teoria delle curve ellittiche, è ancora in fase di sviluppo. PKCS#15: Cryptographic Token Information Format Standard. Propone un modello generalizzato per l’accesso ai token crittografici, al fine di favorire maggiormente la compatibilità tra l’hardware di diverse marche, viene utilizzato ad esempio nelle Smart Card. - 31 - 1.5 La steganografia L’etimologia del termine “steganografia” è data dall’associazione di due parole di origine greca: “stèganos” che significa nascosto, e “grafèin” che significa scrivere, da cui “scrittura nascosta”. Nonostante il significato di steganografia e di crittografia sia molto simile, tra le due tecniche esiste una differenza sostanziale. Lo scopo della crittografia è quello di nascondere il contenuto di un messaggio, cioè fare in modo che lo stesso sia interpretabile solo dagli stretti interlocutori, mentre la steganografia si prefigge di nascondere il messaggio facendo in modo che sia nascosta la comunicazione in sé, cioè solo gli interlocutori sanno di scambiarsi un messaggio e nessun altro. La stenografia nasce dall’esigenza di andare a colmare le lacune che presentano le tecniche crittografiche, si pensi ad esempio ad un soldato che viene sorpreso a scambiare messaggi cifrati con un governo ostile: indipendentemente dal contenuto del messaggio, il solo fatto che vengano scambiati messaggi cifrati desta ovvi sospetti. Esempi sull’uso della steganografia risalgono all’epoca dell'impero persiano. Erodoto, racconta la storia di un nobile persiano che fece tagliare a zero i capelli di uno schiavo fidato al fine di poter tatuare un messaggio sul suo cranio; una volta che i capelli furono ricresciuti, inviò lo schiavo alla sua destinazione, con la sola istruzione di tagliarseli nuovamente. Esempi più elaborati sono le griglie di Cardano che sono fogli di materiale rigido nei quali venivano ritagliati fori rettangolari ad intervalli irregolari dove l’apposizione su un testo né mostrava il messaggio nascosto in quanto bastava leggere solo i caratteri contenuti in ogni foro, oppure la tecnica delle cifre nulle che venne utilizzata nella seconda guerra mondiale dalle truppe tedesche, la quale consisteva nell'inserire il messaggio nascosto in un altro messaggio di testo, in pratica il messaggio trasmesso veniva composto in modo tale che, unendo le lettere secondo un preciso schema (prime lettere di ogni capoverso, seconde lettere di ogni parola, etc), faceva ottenere il messaggio nascosto. Un'altra tecnica molto usata dai nazisti era quella dei micropunti fotografici, i quali erano fotografie della dimensione di un punto dattiloscritto che, una volta sviluppate e ingrandite, potevano diventare pagine stampate di buona qualità. In tempi recenti la steganografia viene usata per inserire in un file digitale un “watermark” che in caso di uso illecito, ad esempio una copia, permette di verificare l’effettiva paternità del file. Un altro tipo di marchio digitale è il “fingerprint” che sono inseriti in diverse copie nello stesso file per distribuirlo ad utenti diversi. Il motivo è quello di ottenere una specie di numero seriale sul file che rende possibile al proprietario l’identificazione degli utenti che lo hanno distribuito a terzi, ad esempio gratuitamente. - 32 - 1.5.1 Steganografia iniettiva e generativa La steganografia si basa sull’esistenza di due messaggi: uno detto messaggio contenitore e l’atro detto messaggio segreto. Il compito del messaggio contenitore è quello di incapsulare il messaggio segreto in modo tale che nessuno, all’infuori degli interlocutori, sappia che il messaggio segreto sia nascosto dentro quello contenitore. L’evoluzione delle tecniche informatiche ha favorito lo sviluppo di algoritmi steganografici sempre più sofisticati, sicuri e pratici da usare, ed ha allargato le tipologie di messaggi contenitori, infatti si potranno usare ogni tipo di files presenti su un computer, anche se solitamente si usano immagini, files sonori e files video. In base all'origine del file contenitore si possono distinguere due tipi di algoritmi steganografici: • algoritmi di steganografia iniettiva; • algoritmi di steganografia generativa. Gli algoritmi di steganografia iniettiva consentono di “iniettare” il messaggio segreto all'interno di un messaggio contenitore già esistente modificandolo in modo tale che sia in grado di contenere il messaggio nascosto, in più devono cercare di rendere il messaggio steganografato indistinguibile dall'originale, in maniera tale da non produrre sospetti a chi potrebbe esaminare la comunicazione. Gli algoritmi di steganografia generativa, partendo dal messaggio segreto producono un opportuno contenitore atto a nascondere nel migliore dei modi il messaggio segreto. 1.5.2 Modelli steganografici Le tecniche steganografiche possono essere suddivise in tre categorie: • steganografia sostitutiva; • steganografia selettiva; • steganografia costruttiva. 1.5.2.1 Steganografia sostitutiva La steganografia sostitutiva è la tecnica più diffusa, al punto che quando si parla di steganografia in generale ci si riferisce ad essa. La steganografia sostitutiva si basa sul fatto che la maggior parte dei canali di trasmissione introducono un rumore ai messaggi che vi scorrono. Figura 14. Effetto sulla trasmissione di un messaggio in un canale rumoroso - 33 - Gli algoritmi di steganografia sostitutiva si basano sulla sostituzione del rumore introdotto dal canale con il messaggio che si vuole nascondere e quindi, a meno di conoscere la chiave segreta, è indistinguibile dal rumore vero e proprio, e quindi può essere trasmesso senza destare sospetti. L’introduzione di rumore nei files che vengono usati come contenitori proviene dal fatto che essi, se prendiamo ad esempio immagini, files sonori e files video, vengono informatizzati dalla realtà attraverso una conversione analogico digitale, è quindi in questa fase di acquisizione che si introduce il rumore. Le immagini vengono digitalizzate per mezzo di scanner e fotocamere, i files sonori attraverso schede di acquisizione audio, mentre i files video utilizzando videocamere. In pratica gli algoritmi di steganografia sostitutiva vanno a sostituire i bit meno significativi dei files digitalizzati con i bit che costituiscono il messaggio segreto in quanto i bit meno significativi corrispondono ai valori meno evidenti di una misura, cioè proprio quelli che possono essere facilmente affetti da errore. Il messaggio segreto viene introdotto nel file contenitore attraverso un'iniezione steganografica, il file risultante si presenta in tutto e per tutto simile all’originale, con differenze difficilmente percettibili ai sensi umani e quindi, a meno di confronti approfonditi con il file originale, utilizzando tecniche apposite che verranno descritte successivamente, è difficile valutare se le eventuali perdite di qualità siano da imputare al rumore od alla presenza di un messaggio segreto steganografato, in più si può notare che spesso il file originale non è disponibile all’analista quindi l’effettuazione di metodologie di confronto risulta impraticabile. 1.5.2.2 Steganografia selettiva La steganografia selettiva è una tecnica puramente teorica e non viene utilizzata quasi mai in pratica. Gli algoritmi di steganografia selettiva si basano sull'idea di procedere per tentativi fino a quando non si riesce a trovare un file contenitore che sia adatto a contenere un certo messaggio segreto senza doverlo modificare o introdurre una variazione sul rumore. Questa tecnica non viene impiegata perché è molto dispendiosa in termini di tempo, ed oltretutto permette di nascondere una quantità d'informazione molto modesta, infatti aumentando la lunghezza del messaggio nascosto diventerebbe sempre più elevata la possibilità di trovare un file contenitore adeguato. - 34 - 1.5.2.3 Steganografia costruttiva La steganografia costruttiva, come la steganografia sostitutiva, inietta il messaggio nascosto nel file contenitore ma nel modificare il file contenitore tiene conto di un modello di rumore, cioè non va a modificare le proprietà intrinseche del file contenitore rendendo il file stenografato estremamente simile a quello originale. Questa tecnica sembrerebbe la migliore, ma in realtà la costruzione del modello di rumore è molto complicata ed immaginando che un possibile attaccante conosca un modello più elaborato per lui sarebbe semplice individuare il messaggio nascosto. Inoltre, se un possibile attaccante venisse in possesso del modello di rumore utilizzato, lo potrebbe analizzare ottenendo la chiave di lettura per tutti i files steganografati con quel modello. 1.5.3 La steganografia applicata ai files digitali In questo paragrafo verranno descritti i principali algoritmi steganografici che sono applicati a diverse tipologie di files. Verranno essenzialmente presi in esame: • files Bitmap; • files Wave; • files compressi con algoritmi loss less e lossy; in modo da rendere evidente la differenza che intercorre fra la manipolazione di un file non compresso con uno compresso. - 35 - 1.5.3.1 Steganografia dei files Bitmap Bitmap è un tipo di formato dati utilizzato per la rappresentazione di immagini raster sui sistemi operativi Microsoft Windows e OS/2, fu introdotto con Windows 3.0 nel 1990. I files Bitmap, hanno generalmente l'estensione .bmp, o meno frequentemente .dib (device-independent bitmap). L’immagine è costituita da una successione di pixel a cui si fa corrispondere un colore definito in una tavolozza (palette). Il numero di bit che costituiscono il colore determina la “profondità” dell’immagine. Per files di profondità maggiore di 8 bit i colori non sono indicizzati ma si utilizza la codifica RGB. La codifica RGB è stata descritta nel 1931 dalla CIE (Commission Internationale dell’Enclairage), è un modello di rappresentazione dei colori di tipo additivo che si basa sui tre colori fondamentali: rosso (Red), verde (Green) e blu (Blue). Per sintesi additiva si intende che unendo i tre colori fondamentali con la loro intensità massima si ottiene il colore bianco. Un file Bitmap ha la seguente struttura: • BITMAPFILEHEADER: contiene informazioni sulla grandezza in byte del file e l'offset del primo byte nella mappa dei pixel; • BITMAPINFOHEADER: contiene le dimensioni in pixel dell'immagine e il numero di colori utilizzati, sono indicate inoltre la risoluzione orizzontale e verticale del dispositivo di output che uniti alla larghezza e all'altezza in pixel, determinano le dimensioni di stampa dell'immagine in grandezza reale; • RGBQUAD: contiene la rappresentazione di tutti i pixel che compongono la Bitmap. Ora si esamineranno in modo accurato le varie sezioni della struttura indicando offset e descrizione dei vari bit. Il BITMAPFILEHEADER è costituito da: Byte iniziale 1 Dimensione 3 4 7 2 9 2 11 4 2 Descrizione bfType (Identificatore di formato) Deve essere settato a “BM” in codifica ASCII per dichiarare che è un file Bitmap bfSize (Grandezza complessiva del file) Specifica la grandezza del file in bytes bfReserved1 (Riservato1) Deve essere settato sempre a 0 bfReserved2 (Riservato2) Deve essere settato sempre a 0 bfOffBits (Lunghezza intestazione) Specifica l'offset del primo byte nella mappa dei pixel Tabella 2. Descrizione del BITMAPFILEHEADER - 36 - Il BITMAPINFOHEADER è costituito da: Byte iniziale 15 Dimensione Descrizione 4 19 4 23 4 27 2 29 2 31 4 35 4 39 4 43 4 47 4 51 4 biSize (Dimensione del BITMAPINFOHEADER) Specifica la dimensione in bytes del BITMAPINFOHEADER biWidth (Larghezza della Bitmap) Specifica la larghezza della Bitmap in bytes biHeight (Altezza della Bitmap) Specifica l’altezza della Bitmap in bytes biPlanes (Numero di pani del dispositivo di output) Deve essere settato sempre a 0 biBitCount (Contatore di bit) Specifica il numero di bits per pixel, i valori accettati sono: 1 (bianco e nero), 4 (16 colori), 8 (256 colori), 24 (16,7 milioni di colori) biCompression (Tipo di compressione) Viene settato a 0 se non c’è compressione biSizeImage (Dimensione della Bitmap) Specifica la dimensione dell’immagine in bytes, se non c’è compressione deve essere settato a 0 biXPelsPerMeter (Risoluzione Orizzontale) Specifica la risoluzione orizzontale in pixel per metro del dispositivo di output biYPelsPerMeter (Risoluzione Verticale) Specifica la risoluzione verticale in pixel per metro del dispositivo di output biClrUsed (Numero di colori usati) Specifica il numero di colori usati nella Bitmap, se è settato a 0 il numero di colori viene calcolato usando il bBitCount biClrImportant (Numero dei colori importanti) Specifica il numero di colori che sono importanti per la Bitmap, se è settato a 0 tutti i colori sono importanti Tabella 3. Descrizione del BITMAPINFOHEADER - 37 - Il RGBQUAD è costituito da: Byte iniziale 1 Dimensione 2 1 3 1 4 1 1 Descrizione rgbBlue (Componente di colore blu) Specifica la parte blu del colore rgbGreen (Componente di colore verde) Specifica la parte verde del colore rgbRed (Componente di colore rosso) Specifica la parte rossa del colore rgbReserved (Riservato) Deve essere settato a 0 Tabella 4. Descrizione del RGBQUAD La numerazione dell’offset è stata fatta ripartire da 1 ma in realtà è seguente a quella del BITMAPINFOHEADER. Da notare il fatto che la struttura della RGBQUAD descritta si avvale della codifica RGB, se invece si fosse utilizzata una tavolozza non sarebbe stato possibile definirne una, in quanto ogni colore avrebbe un codice a se non definibile attraverso una codifica universale. Dopo aver definito in modo rigoroso la struttura di una Bitmap si può affermare che non considerando l’intestazione, essa può essere definita come una matrice M ⋅ N dove M ed N sono rispettivamente la larghezza e l’altezza in pixel dell’immagine. La dimensione totale dell’immagine sarà data dalla formula 54 + ( M ⋅ N ⋅ P ) dove 54 sono i bytes dell’intestazione e P è la profondità dell’immagine in bytes. Una cosa particolare pertinente la dimensione dell’immagine riguarda il fatto che il formato Bitmap prevede che ogni riga sia rappresentata con un numero di bytes multiplo di 4 e gli eventuali bytes aggiuntivi avranno valore 0. Nel corso della trattazione successiva si farà riferimento ad immagini Bitmap a 24bit. Ad esempio per una immagine 2 ⋅ 2 si avranno: 54 bytes per l’intestazione a cui vanno aggiunti 2 ⋅ 3 = 6 bytes della prima riga, che diventano 8 bytes (multiplo di 4), a cui si sommano altri 2 ⋅ 3 = 6 bytes della seconda riga, che diventano 8 bytes, cioè un totale di 54 + 8 + 8 = 70 bytes. Si immagini di avere l’immagine 2 ⋅ 2 , esaminata in precedenza, tutta bianca, quindi per la codifica RGB tutte le componenti di colore fondamentale avranno la massima intensità, non considerando l’intestazione, il file avrà la seguente struttura: - 38 - N. byte B1 B2 B3 B4 B5 B6 B7 B8 B9 B10 B11 B12 B13 B14 B15 B16 Sequenza decimale originale 255 255 255 255 255 0 0 255 255 255 255 255 255 255 0 0 Sequenza binaria originale 11111111 11111111 11111111 11111111 11111111 00000000 00000000 11111111 11111111 11111111 11111111 11111111 11111111 11111111 00000000 00000000 Tabella 5. Codifica binaria e decimale di una bitmap 2 ⋅ 2 tutta bianca Si ricorda che i pixel sono rappresentati da sinistra a destra e dal basso verso l’alto e la sequenza RGB è invertita, per cui sarà BGR. A questo punto si pensi di voler nascondere un messaggio segreto dentro l’immagine contenitore, utilizzando un algoritmo di steganografia. Ad esempio se si volesse nascondere il carattere “A” ed il carattere “B” in codifica ASCII (A=01000001 e B=01000010) si andrà a modificare in ogni bytes dell’immagine contenitore il bit meno significativo ottenendo questo risultato: - 39 - N. byte B1 B2 B3 B4 B5 B6 B7 B8 B9 B10 B11 B12 B13 B14 B15 B16 Sequenza Sequenza Sequenza Sequenza Sequenza Sequenza decimale binaria decimale binaria binaria binaria originale Originale modificata modificata di “A” di “B” 255 11111111 254 11111110 0 255 11111111 255 11111111 1 255 11111111 254 11111110 0 255 11111111 254 11111110 0 255 11111111 254 11111110 0 0 00000000 0 00000000 0 0 00000000 1 00000001 1 255 11111111 254 11111110 0 255 11111111 255 11111111 1 255 11111111 254 11111110 0 255 11111111 254 11111110 0 255 11111111 254 11111110 0 255 11111111 254 11111110 0 255 11111111 254 11111110 0 0 00000000 1 00000001 1 0 00000000 0 00000000 0 Tabella 6. Confronto tra la bitmap originale e quella steganografata Come si può notare confrontando la sequenza originale con quella modificata la variazione sui colori è minima. Per calcolare la dimensione necessaria all’inserimento di un messaggio nascosto M ⋅ N ⋅3 di lunghezza in byte L bisogna risolvere la seguente equazione: L = 8 dove al denominatore si ha la quantità che esprime il numero di byte del file contenitore necessari per contenere un carattere del messaggio nascosto. Per un file 2 ⋅ 2 risolvendo l’ equazione si verifica che è possibile nascondere al massimo 2 caratteri, come era anche intuibile dall’esempio sopra. Sfruttando solo il bit meno significativo di ogni byte diventa estremamente palese che servirebbe un file contenitore di dimensione molto elevata, per sopperire a questo problema si può pensare di sostituire nel file contenitore i 2 bit meno significativi o i 4 bit meno significativi aumentando così la lunghezza massima M ⋅ N ⋅3 come mostrato dalle rispettive equazioni a L = = 4 caratteri e 3 M ⋅ N ⋅3 L= = 8 caratteri. 1, 5 Tuttavia aumentare la possibile dimensione del messaggio segreto utilizzando non più il singolo bit meno significativo di ogni byte, ma i due, tre o quattro bit meno significativi produce ovviamente una diminuzione della qualità dell’immagine e quindi una differenza maggiore fra l’immagine steganografata e l’immagine originale incrementando così la possibilità di destare sospetti. Quindi si deve trovare un giusto punto di equilibrio fra la lunghezza del messaggio nascosto e la - 40 - qualità dell’immagine ad esempio testando che la differenza di luminosità tra un pixel e quelli che lo circondano non sia troppo alta. 1.5.3.2 Steganografia dei files Wave Il WAVEform audio format è un formato audio sviluppato dalla Microsoft e dalla IBM come variante del formato RIFF, capace di gestire vari tipi di frequenze di campionamento, di canali audio e compressioni, la sua estensione è “wav”. Il Wave è costituito da sequenze di byte incapsulate in blocchi detti “chunks”. Un file Wave è costituito da tre blocchi: • RIFF WAVE Chunk: descrive il fatto che il file RIFF concerne un file Wave che richiede due sotto blocchi: il Format Chunk e il Sound Data Chunk; • Format Chunk: contiene la descrizione sul tipo di formato che verrà usato nel Sound Data Chunk; • Sound Data Chunk: contiene informazioni sulla lunghezza del suono ed la codifica del suono stesso. Il RIFF WAVE Chunk è costituito da: Byte iniziale 0 Dimensione Descrizione 4 4 4 8 4 ChunkID (Identificatore del blocco) Deve essere settato a “RIFF” in codifica ASCII ChunkSize (Dimensione del blocco) Specifica la lunghezza del resto del blocco, in pratica è la lunghezza dell’intero file in byte meno 8 byte per i due campi ChunkID e ChunkSize. In pratica sarà settato a: 4 + (8 + SubChunk1Size) + (8 + SubChunk2Size) Format (Formato del blocco) Deve essere settato a “WAVE” in codifica ASCII Tabella 7. Descrizione del RIFF WAVE Chunk - 41 - Il Format Chunk è costituito da: Byte iniziale 12 Dimensione 16 4 20 2 22 2 24 4 28 4 32 2 34 2 36 2 38 4 Descrizione Subchunk1ID (Identificatore del sotto blocco 1) Deve essere settato a “fmt ” in codifica ASCII Subchunk1Size (Dimensione del sotto blocco 1) Specifica la lunghezza del resto del blocco. Se la codifica usata è la PCM deve essere settato a 16 AudioFormat (Formato audio) Specifica il tipo di formato audio. Deve essere settato a 1 se la codifica usata è la PCM altrimenti valori maggiori di 1 indicano che è stata effettuata compressione NumChannels (Numero di canali) Specifica il numero di canali. Deve essere settato a 1 se il suono è mono, a 2 se il suono è stereo, etc SampleRate (Frequenza di campionamento) Specifica la frequenza di campionamento del suono ByteRate Deve essere settato a: SampleRate ⋅ NumChannels ⋅ BitsPerSample 8 BlockAlign Deve essere settato a: NumChannels ⋅ BitsPerSample 8 BitsPerSample (Bit per campione) Specifica il numero di bit per campione. ExtraParamSize Viene utilizzato solo se la codifica non è PCM ExtraParams Viene utilizzato solo se la codifica non è PCM Tabella 8. Descrizione del Format Chunk - 42 - Il Sound Data Chunk è costituito da: Byte iniziale 1 Dimensione 5 4 9 4 Descrizione Subchunk2ID (Identificatore del sotto blocco 2) Deve essere settato a “data” in codifica ASCII Subchunk2Size (Dimensione del sotto blocco 2) Specifica la lunghezza del resto del blocco. Deve essere settato a: NumSample ⋅ NumChannels ⋅ BitsPerSample 8 Data (Dati) Deve essere settato con la sequenza di bit che costituisce il suono Tabella 9. Descrizione del Sound Data Chunk La numerazione dell’offset è stata fatta ripartire da 1 ma in realtà è seguente a quella del Format Chunk. Per comprendere come è strutturato il campo Data del Sound Data Chunk si prenda ad esempio un suono mono, campionato a 44100 Hz a 16 bit. In fase di digitalizzazione, il suono sarà formato da 44100 stringhe di 16 bit al secondo, quindi nel campo Data si avrà una successione di campioni lunghi 16 bit. Nel caso di un suono stereo campionato a 44100 Hz a 16 bit, le stringhe di 16 bit ottenute sono due, una per il canale destro ed una per il sinistro, quindi nel campo Data ogni campione sarà costituito da due sottocampioni lunghi 16 bit, uno per il canale destro e l’altro per il canale sinistro. Nel caso di codifica PCM il file Wave avrà dimensione totale data dalla formula 36 + ( Bits per campione ⋅ Frequenza di campionamento ⋅ Numero di canali ⋅ Tempo ) dove 36 sono i bytes che costituiscono la dimensione del RIFF WAVE Chunk e del Format Chunk, la frequenza di campionamento è definita in Hz ed il tempo è definito in secondi. Come nel caso del file Bitmap è evidente che per applicare un algoritmo di steganografia basterà sostituire i bit meno significativi di ogni campione. Per calcolare la dimensione necessaria all’inserimento di un messaggio nascosto di lunghezza in byte L bisogna risolvere la seguente equazione: Bits per campione ⋅ Frequenza di campionamento ⋅ Numero di canali ⋅ Tempo L= d dove d è il numero di byte del file contenitore necessari per contenere un carattere del messaggio nascosto. - 43 - 1.5.3.3 Steganografia dei files compressi I formati esaminati precedentemente, il file Bitmap ed il file Wave, hanno la caratteristica di essere dei formati molto pesanti e per questo non sono molto usati per le trasmissioni su Internet. Per ovviare a questo problema sono stati sviluppati degli algoritmi di compressione che diminuiscono la dimensione del file originale cercando di preservare le caratteristiche visive o sonore del file a seconda che siano immagini o suoni. Gli algoritmi di compressione si suddividono in due categorie: • algoritmi loss less: comprimono il file originale senza causare perdita di qualità, sono quindi invertibili; • algoritmi lossy: comprimono il file originale causando una diminuzione della qualità e sono per questo non invertibili. Un tipico esempio di file in cui viene utilizzato l’algoritmo loss less LZW (Lempel Ziv Welch) è il formato per le immagini GIF (Graphics Interchange Format). Nei files GIF, ogni pixel viene rappresentato da un singolo byte che identifica un colore della palette fra i 256 possibili. In questo tipo di files non è possibile applicare l’algoritmo di steganografia elaborato in precedenza. Un primo algoritmo consiste nell’acquisire un'immagine con una profondità di colore qualsiasi, limitare il numero di colori ad un numero inferiore a 256 cercando di mantenere comunque una buona qualità visiva, convertire l’immagine così ottenuta in GIF completando la palette con dei colori molto simili a quelli rimasti. In questo modo sarà possibile rappresentare un colore in due modi, con il colore originale oppure con il colore aggiunto simile all'originale. Ora per iniettare un messaggio nascosto basterà controllare nella GIF dove esiste la possibilità di scelta fra più colori interscambiabili. Se le alternative sono due si potrà nascondere un solo bit: se il bit è 0 si sceglierà la prima, se è 1 la seconda; se le alternative sono quattro si potranno nascondere due bit: 00 per la prima, 01 per la seconda, 10 per la terza, 11 per la quarta e così via. Questa tecnica non è molto sicura in quanto è facile individuare sottoinsiemi di colori simili che potrebbero evidenziare la presenza di un messaggio steganografato. Un secondo algoritmo più sicuro del precedente sfrutta la struttura della palette della GIF. Essa si compone di 256 colori, di cui non è importante l’ordine in cui i medesimi compaiono è quindi possibile ottenere 256! permutazioni e questo a patto di cambiare la sequenza dei puntatori dei colori permette di rappresentare una stessa immagine GIF in 256! modi diversi. In questa maniera sarà possibile codificare log 2 ( 256!) = 1683 bit , cioè 210 byte, e questo indipendentemente dalle dimensioni dell’immagine, in quanto basterà semplicemente permutare in modo opportuno la palette. Questo algoritmo risulta essere molto conveniente se il file contenitore risulta essere di piccole dimensioni. - 44 - Un terzo algoritmo, interessante per la tecnica usata, non va a modificare un file contenitore già esistente ma genera una immagine frattale, utilizzando ad esempio quelle dell’insieme di Mandelbrot attraverso una funzione matematica apposita. L’immagine creata è una GIF ed il messaggio segreto viene nascosto per mezzo della palette. Tipici esempi di files in cui vengono applicati algoritmi distruttivi sono il file JPEG per le immagini ed il file MP3 per l’audio. Nei files JPEG viene applicato un algoritmo di compressione che può essere schematizzato come in figura: Figura 15. Schema che rappresenta l’algoritmo di compressione JPEG L’immagine originale, usualmente codificata con uno spazio di colore RGB, viene convertita in codifica YUV che scompone l’informazione relativa a ciascun pixel in due componenti: la luminanza (Y), che definisce il grado di luminosità nel range da nero a bianco, e la crominanza (UV), che definisce il colore in base al rapporto tra due assi, uno che va dal blu al giallo (U) e l’altro che va dal rosso al verde (V). Poiché l’occhio umano è più sensibile alla luminanza piuttosto che alla crominanza, l’algoritmo va a diminuire il grado di definizione della crominanza lasciando inalterata la luminanza, riducendo fino a metà la dimensione del file iniziale, introducendo però perdita di informazione. A questo punto a ciascun blocco di 64 pixel viene applicata la DCT, acronimo di Discrete Cosine Transform, cioè un insieme di operazioni matematiche che trasformano i valori di luminosità e colore in valori di frequenza, in pratica i valori dei pixel contenuti nei blocchi di 8 ⋅ 8 presi dall’immagine originale che variano da 0 a 255 vengono trasformati in frequenze che variano da -721 a 721. Ciascuno dei 64 valori di frequenza viene diviso per uno specifico coefficiente di quantizzazione ed il valore approssimato all’intero più vicino, introducendo perdita di informazione. I coefficienti hanno un peso in modo tale da preservare le frequenze che l’occhio umano percepisce in modo maggiore, quindi le frequenze più basse memorizzate nell’angolo superiore di ogni blocco vengono mantenute, mentre le altre vengono eliminate. I valori ottenuti dal passaggio precedente vengono compressi attraverso un algoritmo loss less, utilizzando l’algoritmo di Huffman o il Q-coding. Infine vengono inseriti nel file la tabella contenente i coefficienti di quantizzazione ed i valori della codifica entropica. - 45 - Una possibile applicazione di un algoritmo steganografico ad una immagine JPEG si basa sul fatto che la matrice dei coefficienti ottenuti dalla DCT è un insieme di valori non interi, e poiché la loro rappresentazione informatica è intera deve essere applicato un processo di arrotondamento, proprio con esso, modificandolo appositamente è possibile inserire un messaggio nascosto. Questo algoritmo risulta essere molto robusto in quanto riesce ad evitare la perdita del messaggio nascosto anche se il file viene compresso nuovamente con un algoritmo lossy. La codifica MP3 sfrutta la psico-acustica per eliminare la ridondanza che esiste in un segnale audio. L’orecchio umano, come studiato da Fletcher e Munson, risulta essere maggiormente sensibile alle frequenze comprese tra 2 e 5 kHz. Figura 16. Soglia di udibilità di un singolo tono Quindi è possibile eliminare dallo spettro del segnale in analisi le frequenze che comunque l’orecchio umano medio non percepirebbe: le bassissime e le alte frequenze. Un altro fattore che deve essere esaminato è il cosiddetto mascheramento. Si parla di mascheramento nel dominio della frequenza quando un suono di debole intensità ad una certa frequenza viene sovrastato da un suono ad alta intensità nei pressi della stessa frequenza, nell’ordine di qualche centinaio di Hz e l’orecchio umano riesce a percepire solo il secondo. Si parla di mascheramento nel dominio del tempo, quando ad esempio si hanno due suoni, uno debole ed uno forte nei pressi della stessa frequenza, quando quello forte cessa, l’orecchio umano non percepisce immediatamente il suono debole, ma dovrà trascorrere un certo tempo di latenza, in quanto la membrana del timpano deve assestarsi. In conclusione, l’effetto del mascheramento permette di eliminare una grande quantità di spettro del segnale audio perchè non udibile. Per codificare un file audio in MP3, si suddivide il segnale in 32 sottobande attraverso dei filtri, per ognuna delle stesse viene calcolato il valore per la soglia di mascheramento nel tempo ed in frequenza relativi alle bande adiacenti. Se il segnale audio è stereo si riesce ad eliminare ancora della ridondanza dovuta al fatto che l’orecchio umano al di sotto di certe frequenze non percepisce la posizione spaziale dei suoni, quindi è possibile per le stesse usare il segnale ad un unico canale. A questo punto vengono effettuate le operazioni di quantizzazione in modo tale da introdurre il minor rumore possibile e di codifica entropica con algoritmi loss less, - 46 - nella fattispecie si utilizza l’algoritmo di Hufmann. Per limitare il disturbo dovuto alla quantizzazione vengono applicati dei fattori di scala ad ogni banda. Infine si crea il file immagazzinando il flusso di bit in uscita. Come per il file JPEG, per applicare un algoritmo steganografico al file MP3 si procede andando a modificare i valori ottenuti dalla quantizzazione a cui sono stati applicati i fattori di scala per limitare il rumore da loro introdotto. 1.5.4 Stima sulla sicurezza degli algoritmi steganografici La steganografia, ha lo scopo di occultare la trasmissione del messaggio segreto, nel senso che un possibile attaccante non dovrebbe neanche sapere che si sta effettuando una trasmissione. Seguendo il principio di robustezza proposto da Kerckhoff per cui la sicurezza del sistema deve basarsi sull'ipotesi che il “nemico” abbia piena conoscenza dei dettagli di progetto e sull’implementazione del sistema stesso, tutti gli algoritmi proposti non sono sicuri. Per aumentare la sicurezza di una trasmissione si può pensare di usare in modo combinato la steganografia e la crittografia, cioè il messaggio segreto viene prima cifrato e poi occultato in un file contenitore. Però questa soluzione non aumenta in sé la robustezza dell’algoritmo steganografico in quanto solo il fatto che un possibile attaccante sia riuscito a sapere che il file contiene un messaggio segreto rende minima la robustezza dell’algoritmo utilizzato. Come per la crittografia esiste una scienza che studia il modo di svelare le procedure steganografiche chiamata stegoanalisi. Le principali tecniche di stegoanalisi sono: • stego only: lo stegoanalista dispone solo del file steganografato e deve cercare di svelare il messaggio nascosto; • know cover: lo stegoanalista dispone sia del file steganografato che del file contenitore, può quindi effettuare dei confronti in modo da cercare differenze tra i due files; • know message; lo stegoanalista dispone sia del file steganografato sia del messaggio nascosto, può quindi cercare di scoprire il metodo utilizzato per nascondere l’informazione segreta; • chosen stego: lo stegoanalista dispone sia del file steganografato che dell’algoritmo di steganografia utilizzato; • chosen message; lo stegoanalista genera alcuni files steganografati servendosi di opportuni strumenti steganografici partendo da un messaggio preciso, cercando degli elementi che lo aiutino ad identificare l’uso di un metodo specifico. L’attacco know cover risulta essere il più efficace in quanto se i due files che dovrebbero essere uguali presentano delle differenze, potrebbe significare che ci sia un messaggio nascosto, anche se la differenza potrebbero essere dovuta semplicemente da una diversa informatizzazione e quindi dal diverso rumore introdotto. - 47 - Tenuto conto delle possibili tecniche di stegoanalisi per migliorare la robustezza di un algoritmo bisognerà seguire alcune regole: • mai usare come files contenitore, files pubblici o facilmente reperibili; • mai usare più volte lo stesso file come contenitore; • distruggere i files originali dopo averli usati; poichè darebbe allo stegoanalista la possibilità di effettuare un attacco know cover. Un altro punto cruciale per valutare la robustezza di un algoritmo è il fatto che una possibile compressione del file steganografato possa cancellare il messaggio segreto, quindi gli algoritmi proposti per i files Bitmap e Wave sono da questo punto di vista inutilizzabili, mentre risultano essere abbastanza robusti gli algoritmi utilizzati sui files JPEG e MP3 se accortamente progettati. L’algoritmo che sfrutta la modifica della palette sui files GIF risulta essere robusto sotto questo punto di vista ma è banalmente riconoscibile da un attacco chosen stego. Le metodologie pratiche che si utilizzano per effettuare controlli sulle immagini sono due: • analisi visuali; • analisi statistiche. Le analisi visuali sfruttano degli algoritmi di filtering che riescono a rendere evidente la presenza di un messaggio nascosto ma risultano essere molto lente se la mole di file da esaminare è notevole. Le analisi statistiche confrontano la distribuzione di frequenza dei colori in un potenziale file steganografato con la distribuzione di frequenza teoricamente attesa per un file steganografato, per scoprire la presenza di un eventuale messaggio nascosto. - 48 - 1.6 I protocolli web In questo paragrafo verranno descritti i protocolli http e https. Verrà in primo luogo fornita una breve storia del loro sviluppo, dopodichè verranno spiegate le modalità di scambio dei dati utilizzate. Infine saranno esposti pregi e difetti dei due protocolli. 1.6.1 Il protocollo http Http, acronimo di Hyper Text Transfer Protocol, è un protocollo di trasferimento per il world wide web. Fu progettato alla fine del 1980 da Tim Berners-Lee, la versione 0.9 venne utilizzata per la condivisione delle informazioni tra la comunità dei fisici. Nel 1991 fu sviluppata, sempre dallo stesso Bernere-Lee, la versione 1.0 che fu classificata nel 1996 dalla IETF come RFC 1945. Con l’avvento dei browser grafici il protocollo http 1.0 risultò essere inadeguato, in quanto non era in grado di ospitare più siti www su uno stesso server, non permetteva il riuso delle connessioni disponibili e non garantiva una sicurezza adeguata. Nel 1997 si passò quindi alla versione 1.1, presentata come RFC 2068 nel 1997 ed aggiornata nel 1999 come descritto dal RFC 2616. L’http sfrutta il protocollo TCP/IP per fornire i propri servizi di trasmissione. La porta TCP utilizzata usualmente è la 80. Figura 17. Architettura del protocollo http L’http a differenza di protocolli come l’Ftp, il Telnet e l’Smtp, non è stato progettato esplicitamente per trasferire dati specifici ma permette il trasferimento di qualsiasi tipo file. L’http è un protocollo di tipo request/response. Fino alla versione 1.0 la connessione veniva stabilita da un client tramite una request e chiusa dal server per mezzo di una response, con la versione 1.1 dopo la - 49 - response la connessione non viene terminata ma è possibile procedere con una nuova coppia request/response in modo da gestire transazioni multiple. L’introduzione di questo metodo è dovuta alla grande complicatezza delle pagine html, che contengo immagini, suoni, etc, quindi per poterle scaricare avrebbero innescato un numero enorme di request/response singole che avrebbero occupato molte risorse del server mentre usando request/response multiple il server gestisce, ad esempio, il client fino alla consultazione completa dell’intera pagina. La request e la response sono gestite come un messaggio MIME, Multipurpose Internet Mail Extension, ossia come un messaggio di testo, quindi anche immagini, suoni, etc sono trattati come tali, questo perché l’http implementa i protocolli già forniti da Internet a livello più basso. La request è formata da tre campi: • request line; • header fields; • message body. La request line è composta dal metodo di richiesta, dall’URI e dalla versione del protocollo. L’URI, Uniform Resource Identifier, identifica la risorsa su cui è applicata la request. I metodi di richiesta indicano il tipo di operazione che dovrà compiere il server sull’URI. I metodi di richiesta più comuni sono: • GET, viene usato per ottenere il contenuto della risorsa dell’URI, ad esempio una pagina html; • HEAD, viene usato per ottenere l’header dell’URI, ad esempio per controllare la data di modifica di un file, questo metodo non prevede l’uso del body; • POST, viene usato per inviare informazione al server, l’URI identifica cosa si sta inviando ed il body ne racchiude il contenuto, viene ad esempio utilizzato per inviare i dati contenuti in un form di una pagina html. Gli header permettono al client di trasmettere ulteriori informazioni sulla richiesta e su se stesso. Gli header di richiesta più comuni sono: • Host: contiene il nome del server a cui si riferisce l'URI. Con il protocollo http/1.1 risulta essere un campo obbligatorio in quanto permette l'uso dei virtual host; • User-Agent: contiene informazioni sul client: tipo browser, produttore, versione, etc. La response è formata da tre campi: • status line; • header fields; • message body. - 50 - La status line contiene un codice a tre cifre che identifica un messaggio di risposta alla request formato in base al seguente schema, dove il carattere “x” identifica un numero da 0 a 9: • 1xx: Informational: contiene messaggi informativi; • 2xx: Success: indica se la richiesta è stata eseguita con successo; • 3xx: Redirection: contiene indicazioni su come ottenere la richiesta effettuata, poiché il server non è in grado di eseguirla in modo immediato; • 4xx: Client error: contiene informazioni sul tipo di errore eseguito nella richiesta del client che rende impossibile il soddisfacimento dal server; • 5xx: Server error: contiene informazione sul tipo di errore eseguito dal server per cui non riesce a soddisfare la richiesta. Gli header permettono al server di inserire ulteriori informazioni che non sarebbe stato possibile inserire nella status line. Gli header più comuni sono: • Server: contiene informazioni sul server: tipo e versione; • Content-Type: contiene informazioni sul tipo di contenuto restituito. Tali informazioni, dette “Media type”. Per esempio ad una risposta HTML sono frequenti i tipi MIME (RFC 1521): • text/html: identifica un documento html; • text/plain: identifica un documento di testo non formattato; • image/jpeg: identifica un’immagine di formato JPEG. Il protocollo http offre la possibilità gestire la request/response chain ossia, di regolare il percorso logico che seguirà la request/response dal client al server anche tramite l’inserimento di entità intermedie. Una request/response chain può essere modificata da tre entità: • Proxy; • Gateway; • Tunnel. - 51 - Il proxy è una entità software che si trova fra il client ed il server. Quando il client inoltra una request, essa viene indirizzata al proxy che la rinvia al server, dopodichè il server invia la response al proxy che la rinvia al client. Il proxy ha la funzione di caching, ossia memorizza tutte le pagine che i client richiedono e quelle raggiungibili dalle stesse, poichè hanno un’alta probabilità di essere richieste, quindi l’utilizzo del proxy risulta vantaggioso quando la linea tra esso ed il server è più veloce rispetto a quella tra il client ed il server. Da notare è il fatto che la connessione risulta essere sempre client-proxy, qualunque sia il server finale. Figura 18. Connessione http con proxy Il gateway è un’entità che si trova tra il client ed il server, ma a differenza del proxy, il client vede il gateway come il server finale. Il gateway prende la request inviata dal client verso il server interessato, attende la response e la rinvia al client, in modo trasparente. Il gateway risulta essere vantaggioso quando ci si trova a comunicare con una serie di server che rispondono allo stesso nome, infatti esso smisterà le varie request ai server opportuni facendo percepire al client di comunicare con un unico server e quindi ottimizzando la connessione. Figura 19. Connessione http con gateway Il tunnel è un’entità che permette lo scambio tra due collegamenti senza modificare la comunicazione; è usato quando la comunicazione necessita di - 52 - passare da un punto ad un altro attraversando un’entità che non è in grado di trasmettere tale messaggio. Un esempio di utilizzo del tunnel avviene quando sulla rete è presente un firewall. Il tunnel permette di instaurare una connessione punto-punto ai capi del firewall, in modo da far “vedere” al client il server che altrimenti sarebbe stato nascosto, quindi il tunnel viene attivato solo quando avviene una request per il server nascosto, mentre rimane disattivato per le altre richieste. L’utilizzo del tunnel risulta essere trasparente al client, in quanto se la request viene lasciata passare al server, il client avrà la sua response, mentre se il tunnel la blocca il client riceverà un errore dal browser. Figura 20. Connessione http con tunnel Il protocollo http ha essenzialmente tre difetti. Un primo difetto è dovuto al fatto che la connessione TCP, quando viene aperta ha una velocità molto bassa, questa poi aumenta fino ad andare a regime, una transazione request/response spesso non dura abbastanza per raggiungere tale velocità e quindi l’http risulta essere inefficiente. Un miglioramento è stato apportato con le request/response multiple, che aumentando la durata della connessione, aumentano la probabilità di raggiungere la velocità di regime. Un secondo difetto si ha perché l’http usa uno scambio dei dati stateless, cioè non esiste una memoria delle richieste del client e delle risposte del server, quindi se ad esempio si sta compilando una serie di form concatenate e cade la linea quando ancora non è stata terminata la procedura, tutta la compilazione viene cancellata poiché quasi sicuramente alla nuova connessione l’indirizzo IP sarà diverso, oppure potrebbe accadere che ad un altro utente, che si collega allo stesso server appena è caduta la linea dell’altro, venga assegnato l’indirizzo IP precedente dell’altro utente ed esso si ritroverebbe con le form compilate dal precedente utente. Una possibile soluzione è l’utilizzo dei cookies che sono dei file che vengono inviati dal server al client in cui sono contenute informazioni riguardanti la connessione ed il server può richiederle se necessario al client. Il terzo difetto deriva dal fatto che la trasmissione dati dell’http è in chiaro, quindi risulta relativamente facile osservare lo scambio che avviene tra il client ed il - 53 - server anche quando essa risulta essere riservata, ad esempio nello scambio di credenziali di autenticazione. 1.6.2 Il protocollo https L’https è stato introdotto per migliorare la sicurezza del protocollo http, che attraverso l’utilizzo di tecniche di crittografia e di autenticazione riesce a garantire un elevato livello di protezione nella trasmissione dei dati. L’https in pratica interpone fra il TCP e l’http il protocollo SSL. Figura 21. Architettura del protocollo https Il protocollo SSL, acronimo di Secure Socket Layer, fu sviluppato originariamente da Netscape, successivamente fu migliorato ed adottato come standard per Internet sotto il nome di TLS, Transport Layer Security, come descritto nel RFC 2818. Il protocollo SSL garantisce che solamente il client ed il server siano in grado di conoscere il contenuto della comunicazione. La porta TCP utilizzata usualmente è la 443. Una tipica trasmissione di un messaggio utilizzando il protocollo SSL si basa su due fasi. La prima fase ha la funzione di effettuare l'autenticazione reciproca del client, del server e di scambio della chiave di cifratura da utilizzare nel corso della connessione. La seconda fase è quella vera e propria di trasmissione del messaggio in cui lo stesso viene suddiviso in blocchi di lunghezza prefissata, ciascun blocco viene compresso e gli viene aggiunto il codice MAC atto a garantire l'autenticità dei dati, il blocco complessivo viene cifrato utilizzando un algoritmo simmetrico ed in testa viene posto l’header SSL. Il blocco così composto viene inviato al server tramite il protocollo TCP/IP. - 54 - Il server alla ricezione del blocco effettuerà il procedimento inverso rispetto a quello effettuato dall’utente, quindi eliminerà l’header SSL decifrerà il messaggio utilizzando la chiave opportuna, verificherà che il codice MAC sia quello del client, eliminerà il codice MAC, decomprimerà il blocco risultante ed infine, riunendo i vari frammenti, otterrà il messaggio originale. Riassumendo, la procedura di criptazione si basa su una chiave pubblica che il server invia al client ad ogni connessione, per mezzo di essa vengono generati pacchetti criptati che possono essere letti solo dal server che ha rilasciato la chiave pubblica poiché sarà l’unico host a possedere la chiave privata. L'SSL Handshake protocol coordina gli stati del client e del server, permettendo così ai sistemi operativi di ognuno di operare in modo efficace. Lo stato è rappresentato in modo doppio: una volta come lo stato operativo corrente, e, durante la fase di handshake, come lo stato in attesa; inoltre sono mantenuti separati gli stati di lettura da quelli di scrittura. Una sessione SSL può includere più di una connessione sicura; inoltre le applicazioni possono avere più sessioni simultanee. Lo stato della sessione è costituito dai seguenti elementi: • session identifier: è una sequenza arbitraria di bytes scelta dal server per identificare uno stato attivo della sessione, o comunque riesumabile; • peer certificate: è un certificato X.509 del peer. Può essere nullo; • compression method: specifica l'algoritmo usato per comprimere i dati prima della crittografia; • cipher spec: specifica l'algoritmo di crittografia bulk (ad es. null, DES, etc.) e l'algoritmo MAC (per es. MD5 o SHA). Stabilisce inoltre alcuni parametri dell’algoritmo di cifratura; • master secret: sono 48 bytes segreti, condivisi dal client e dal server; • is resumable: è un flag che indica se la sessione può essere usata per iniziare nuove connessioni. Lo stato della connessione è costituito dai seguenti elementi: • server and client random: è una sequenza di bytes scelti dal server e dal client per ogni connessione; • server write MAC secret: è la sequenza segreta usata nelle operazioni MAC sui dati scritti dal server; • client write MAC secret: è la sequenza segreta usata nelle operazioni MAC sui dati scritti dal client; • server write key: è la chiave per i dati cifrati dal client e messi in chiaro dal server; • initialization vectors: se è usato un blocco cifrato con algoritmo CBC, allora per ogni chiave è mantenuto un vettore d'inizializzazione (IV). Questo campo è inizializzato dal protocollo SSL handshake. Poi il codice cifrato finale di ogni record è salvato per essere usato con i record seguenti; - 55 - • sequence numbers: ognuna delle due parti comunicanti mantiene separati numeri di sequenza per i messaggi spediti e ricevuti per ogni connessione. Quando uno dei due manda o riceve un messaggio di change cipher spec, il numero appropriato della sequenza è posto a zero. I numeri di sequenza sono espressi da 64 bits e non possono quindi eccedere 264-1. Il protocollo SSL Record layer riceve i dati dai livelli superiori senza interpretarli, in forma di blocchi non vuoti di dimensione arbitraria. Esso frammenta i blocchi di informazioni in SSLPlaintext records, di dimensione massima di 214 bytes. L’SSLPlaintext record è costituito da: • ProtocolVersion: specifica la versione del protocollo in uso; • ContentType: è formato da 4 campi: o change_cipher_spec: specifica il cambio di cifratura usato per la comunicazione; o alert: indica eventuali condizioni di errore; o handshake: negoziazione che avviene nella sessione SSL; o application_data: sono i dati trasmessi nella sessione SSL. Quando viene applicato un algoritmo di compressione la SSLPlaintext si trasforma in una SSLCompressed. Tutti i records sono compressi tramite un algoritmo definito nello stato corrente della sessione anche se all'inizio viene definito come CompressionMethod.null. Nel caso in cui venisse usata una CipherSpec i dati relativi agli algoritmi di compressione vengono cancellati dallo stato. L’algoritmo di compressione deve essere di tipo loss less e non può aumentare la lunghezza del contenuto più di 1024 bytes. Se la decompressione incontra un SSLCompressed.fragment che scompattato fosse più lungo di 214 bytes, deve segnalare un fatal decompression_failure alert. Tutti i records vengono protetti utilizzando algoritmi di crittografia e MAC definiti nel CipherSpec. Anche se inizialmente il CipherSpec è settato a SSL_NULL_WITH_NULL_NULL vengono comunque utilizzati gli algoritmi di cifratura e MAC. Le tecniche usate per le operazioni di crittografia e di MAC sono stabilite nel CipherSpec.cipher_type; tali metodi trasformano una struttura SSLCompressed in una SSLCiphertext. La decifratura inverte tale trasformazione. Le trasmissioni includono anche un numero di sequenza, in modo che i messaggi persi, alterati o intrusi siano rilevabili. Possono essere utilizzati due tipi di cifratura, quella a flusso e quella a blocchi. Gli algoritmi stream ciphers convertono le strutture SSLCompressed.fragment in stream SSLCiphertext.fragment e viceversa. Il MAC sarà definito come: hash(MAC_write_secret + pad_2 + hash(MAC_write_secret + pad_1 + seq_num + length + content)) - 56 - dove: • +: denota la concatenazione; • pad_1: è il carattere 0x36 ripetuto 48 volte per MD5 o 40 volte per il SHA; • pad_2: è il carattere 0x5c ripetuto lo stesso numero di volte del precedente; • seq_num: è il numero di sequenza del messaggio; • hash: è l'algoritmo hash definito nella CipherSpec. Concludendo la lunghezza del SSLCiphertext sarà: SSLCiphertext.lenght = SSLCompressed.lenght + CipherSpec.hash_size Gli algoritmi block ciphers, trasformano la struttura SSLCompressed.fragment in un blocco SSLCiphertext.fragment, dove però ci sarà una particolare attenzione ad operazioni di padding, le quali aggiungono caratteri per forzare la lunghezza del plaintext ad essere un multiplo della lunghezza dei blocchi cifrati dall'algoritmo di crittografia. Il protocollo SSL Change cipher spec è finalizzato a segnalare le transizioni nelle strategie di crittografia. Il protocollo consiste di un singolo messaggio, che è cifrato e compresso secondo il Current CipherSpec. Il messaggio consiste di un singolo byte di valore uno. Il messaggio di change cipher spec è mandato sia dal client che dal server per notificare all'altro che da quel momento in poi i records saranno protetti usando le nuove, appena negoziate CipherSpec e chiavi. La ricezione di questi messaggi causa al ricevente la copia del pending read state nel current read state: infatti sia il server che il client mantengono separato questo doppio stato. Quando il client o il server manda un messaggio di change cipher spec, esso copia il pending write state nel current write state. Il client manda allora un messaggio di change cipher spec seguente i messaggi di handshake key exchange and certificate verify, ed il server ne manda uno dopo aver processato con successo il messaggio di key exchange ricevuto dal client. Quando l'handshake è completato il client ed il server si scambiano i messaggi di change cipher spec ed iniziano a comunicare, usando le nuove proprietà appena concordate. Un messaggio di change cipher spec inaspettato provoca un unexpected_message alert. Quando si riesuma una sessione, il messaggio di change cipher spec è spedito subito dopo il messaggio di hello. Il protocollo SSL Alert ha lo scopo di inviare messaggi di alert in caso di anomalie. Ogni alert ha una priorità, ad esempio un messaggio con livello fatal si risolve con l’immediata terminazione della connessione. Come gli altri messaggi anche quelli di alert sono cifrati e compressi come specificato nello stato corrente della connessione. Esistono due tipi di alert: quelli di chiusura e quelli di errore. - 57 - Gli alert di chiusura vengono inviati quando il client ed il server devono comunicarsi che la connessione sta terminando per evitare un attacco. Entrambi possono iniziare lo scambio dei messaggi di chiusura. In particolare il messaggio close_notify che notifica al ricevente che il mittente non trasmetterà più su quella connessione. La sessione diviene non riesumabile se qualche connessione è terminata senza il dovuto messaggio close_notify con livello warning. Gli alert di errore vengono inviati quando uno dei due interlocutori riscontra un errore, allora la parte che lo ha rivelato manda un messaggio all'altra; dopo la trasmissione/ricezione di un messaggio di fatal alert, entrambe le parti immediatamente chiudono la connessione. Sia il server che il client sono obbligati a cancellare ogni identificatore di sessione, chiave, e altri segreti associati con la connessione fallita. I principali messaggi di alert di errore sono: • unexpected_message: identifica che un messaggio inappropriato è stato ricevuto. Questo tipo di allerta è sempre fatale; • bad_record_mac: questa allerta è spedita quando un record è ricevuto con un errato MAC; questo messaggio è sempre fatale; • decompression_failure: identifica che la funzione di decompressione ha ricevuto un errato input, ad esempio dati che si espandono oltre la lunghezza loro assegnata; questo messaggio è sempre fatale; • handshake_failure: indica che il mittente è incapace di negoziare un set accettabile di parametri di sicurezza tra quelli possibili; questo messaggio è sempre fatale; • no_certificate: può essere mandato in risposta ad un certificate request se non è disponibile una certificazione appropriata; • bad_certificate: viene mandato in caso di certificazione errata, contenente una firma che non è verificata correttamente, etc; • unsupported_certicate: identifica una certificazione non supportata; • certificate_revoked: indica che una certificazione è stata revocata dal suo firmatario; • certificate_expired: indica che una certificazione è scaduta o attualmente non valida; • certificate_unknown: indica che un problema non meglio specificato è sorto nel processare la certificazione; • illegal_parameter: indica che un campo dell’handshake è di dimensione errata o inconsistente con altri campi; questo messaggio è sempre fatale. Il protocollo SSL Handshake fornisce i parametri per la crittografia che compongono lo stato della sessione, ed opera interfacciandosi con il protocollo SSL Record Layer. Quando un client ed un server iniziano a comunicare, concordano sulla versione del protocollo, scelgono gli algoritmi di crittografia, si autenticano a vicenda, ed usano la crittografia a chiave pubblica per generare dati segreti condivisi. Questi processi sono eseguiti nel protocollo handshake, in particolare il client spedisce un messaggio di client hello al quale il server deve - 58 - rispondere con un messaggio di server hello, altrimenti si verificherebbe un errore fatale e la connessione fallirebbe. I messaggi client hello e server hello sono usati per stabilire le policy di sicurezza ottenibili fra client e server. Ad esempio il messaggio client hello avrà i seguenti attributi: • protocol version: contiene la versione del protocollo utilizzato; • session ID: contiene un numerio identificativo; • cipher suite: contiene le combinazioni di algoritmi di crittografia supportati dal client, il server sceglierà una cipher suite oppure, se non c'è una scelta accettabile chiuderà la connessione; • compression method: contiene una lista di algoritmi di compressione supportati dal client, se il server non ne supporta nessuno la sessione sarà chiusa. In modo analogo si avranno gli attributi del server hello dove nella cipher suite sarà contenuta la singola cipher suite, scelta dalla lista fornita dal client e nella compression method sarà contenuto il singolo metodo di compressione scelto dal server dalla lista fornita dal client. Inoltre, hanno due valori casuali che vengono generati e scambiati: il ClientHello.random ed il ServerHello.random, i quali devono essere indipendenti l’uno dall’altro. Dopo il messaggio di hello il server spedisce il suo certificato, se deve essere autenticato, oppure invia un messaggio di server key exchange se ad esempio il server non ha certificato, o se il suo certificato è solo per la firma. Se il server viene autenticato, può richiedere un certificato al client se ciò è in accordo con la cipher suite scelta. A questo punto il server manderà il messaggio di server hello done, indicando che la fase hello-message dell'handshake è completa, ed attenderà una risposta dal client. Se il server ha mandato un messaggio di certificate request, il client dovrà spedire o un certificate message o una no certificate alert. Il messaggio di client key exchange verrà mandato adesso, il cui contenuto dipende dall'algoritmo a chiave pubblica selezionato con il client hello ed il server hello. Se il client ha spedito un certificato con abilitazione alla firma, allora un messaggio di certificate verify verrà spedito per verificare esplicitamente il certificato. A questo punto un messaggio di change cipher spec viene mandato dal client, che copia il pending Cipher Spec nel current Cipher Spec. Il client manda immediatamente il messaggio di finished usando i nuovi algoritmi, chiavi e stringhe segrete concordate . Il server risponde con un messaggio di change cipher spec, copia il pending Cipher Spec nel current Cipher Spec, e spedisce il suo messaggio di finished in accordo con la nuova Cipher Spec. In questo modo la fase di handshake è terminata ed il client ed il server possono cominciare a scambiare dati del livello applicazione. Se il client ed il server decidono di riabilitare una precedente sessione o di duplicarne una esistente, il client manderà un client hello usando la Session ID della sessione da riesumare, il server controllerà la sua session cache per trovare una corrispondenza: se questa viene trovata il server manda un server hello con lo - 59 - stesso Session ID. Adesso sia il client che il server devono mandare un messaggio di change cipher spec e procedere direttamente fino al messaggio di finished. Una volta che il ripristino è completo, il client ed il server possono iniziare a scambiarsi dati di livello applicazione. Se il server non trova una corrispondenza di Session ID nella propria session cache, allora il server genera un nuovo Session ID e verrà eseguito un handshake completo. Il protocollo SSL risulta essere non sicurissimo se utilizza algoritmi di cifratura poco robusti, ad esempio l’RC4 con chiave a 40 bits risulta essere poco affidabile poichè alcuni gruppi indipendenti sono riusciti a forzarlo in circa 8 giorni. Un altro difetto del protocollo SSL sta nel fatto che non protegge da attacchi agli host, quindi sarebbe conveniente proteggersi con software di garanzia dell’integrità dei dati come ad esempio Tripwire. Tripwire usa funzioni hash sicure per garantire che i documenti non siano cambiati rispetto ad una versione di riferimento protetta in scrittura, se verifica un eventuale attacco, notifica all’amministratore del sistema con una email l’avvenuta intrusione e ripristina i file alla situazione iniziale. Un ultimo difetto del protocollo SSL, è quello per cui esso riduce le prestazioni del server, in quanto lo stesso si trova a cifrare la pagina web richiesta dal client e se essa è molto pesante si potrebbero riscontrare notevoli rallentamenti o addirittura il timeout della pagina; quindi risulta conveniente progettare le pagine che useranno l’https con poca grafica e più contenuti testuali. - 60 - CAPITOLO 2 In questo capitolo verrà presentata l’azienda presso la quale è stato effettuato il tirocinio. Si illustrerà poi la situazione di partenza in seguito alla quale è stato sviluppato il progetto, evidenziando le specifiche richieste poste in corso d’opera. - 61 - 2.1 La Phi.d’α αlpha s.r.l. La Phi.d'αlpha s.r.l. è un’azienda di consulenza strategica di impresa che si occupa dello sviluppo di sinergie estremamente articolate e mette in comunicazione i vari comparti di una azienda, intervenendo tempestivamente sulle aree deboli al fine di seguire costantemente ed ottimizzare i processi aziendali con soluzioni integrate. In altri termini, la Phi.d'αlpha s.r.l., aiuta le imprese ad organizzarsi e strutturarsi per far fronte al mercato globale partendo dalla individuazione del merito creditizio sino ad arrivare alla valutazione dell'azienda. Essenzialmente, la Phi.d'αlpha s.r.l., si occupa di consulenza finanziaria ed economica, permettendo così alle aziende clienti di creare valore ottemperando i seguenti obiettivi: • ottimizzazione dei flussi di cassa relativi alla gestione operativa; • minimizzazione del livello di rischio aziendale; • minimizzazione nel costo delle fonti di finanziamento. L'attività della Phi.d'αlpha si esplica nelle seguenti possibili fasi di intervento: • analisi andamentale; • analisi quantitativa; • analisi qualitativa e dei rischi operativi. Per analisi andamentale si intende l’analisi dei rapporti che l’azienda cliente instaura con il sistema bancario, quindi si effettua una verifica: dei rapporti bancari tramite web-banking, dei movimenti, dei saldi contabili e liquidi e delle condizioni di tenuta conto, della coerenza tra il numero di banche affidanti e le dimensioni aziendali, studiando i dati storici, attuali ed incrementali. Per analisi quantitativa si intende l’analisi della struttura patrimoniale e dell’equilibrio finanziario dell'azienda, quindi ci si troverà ad analizzare bilanci aziendali in modo da misurare la capacità dell'azienda di svilupparsi, di produrre risorse adeguate e reddito sufficiente per la copertura del debito e di remunerare il capitale di rischio. Per analisi qualitativa e dei rischi operativi si intende l’analisi dell’attività aziendale e della sua presenza sul mercato, studiando la tipologia della clientela e dei fornitori, analizzando rischi aziendali, nella fattispecie anche quelli dovuti ai contratti utilizzati sia per operazioni commerciali sia per rapporti con gli istituti di credito. - 62 - 2.2 Descrizione del progetto Da quanto descritto nel paragrafo precedente è facile evincere che la Phi.d'αlpha s.r.l. si occupa in maniera frequente della trattazione di documenti finanziari ed economici, quindi di dati sensibili e riservati che devono essere trasmessi per la loro natura, con modalità sicure. Fino ad oggi la Phi.d'αlpha s.r.l. ha utilizzato per la trasmissione di tali documenti, o mezzi non multimediali o semplici email. Con l’avvento di nuove normative giuridiche più restrittive e con l’aumento di possibili azioni di spionaggio aziendale dovuto ad hacker, la Phi.d'αlpha s.r.l. ha deciso di dotarsi di un portale web in cui fosse possibile trasmettere documenti di elevata sensibilità in modo sicuro. La Phi.d'αlpha s.r.l. possedeva già un sito web ma non predisposto a tale esigenza, in quanto sviluppato diversi anni orsono risultava essere statico in quanto creato in semplice linguaggio html. Il vecchio sito risiede su un server Microsoft in hosting con supporto database Microsoft Access e linguaggio ASP, implementato su protocollo http. La Phi.d'αlpha s.r.l. ha richiesto esplicitamente di utilizzare tali tecnologie per la realizzazione del loro progetto. Il progetto richiesto consiste nell’implementazione di un portale web ad accesso riservato con controllo condizionato degli accessi e criptazione dei documenti depositati. Essenzialmente la loro esigenza era quella di avere una “zona” su web dove poter condividere in modo sicuro documenti che poi sarebbero potuti essere consultati anche da diversi clienti e collaboratori. La gestione delle credenziali di autenticazione sarà gestita dalla Phi.d'αlpha s.r.l. stessa che, con cadenza trimestrale, fornirà ai suoi clienti e collaboratori un username ed una password per accedere all’area riservata, quindi non sarà implementata una sezione di registrazione in quanto tale area è di possibile accesso ai soli collaboratori o clienti. Punto focale per la realizzazione è il fatto che i clienti ed i collaboratori sono in numero limitato, nell’ordine di qualche decina. Una richiesta essenziale era quella di creare una tecnologia che rendesse il file trasmesso attribuibile solo ad una certa persona. Un requisito fondamentale, sollecitato fin dall’inizio del tirocinio, era quello di realizzare un’interfaccia molto semplice, friendly, utilizzabile anche da persone poco pratiche nell’uso dei computers. Infine è stato richiesto di aggiornare tutto il sito in modo da mantenere i vecchi contenuti ma modificandone la struttura in modo da renderlo più dinamico, per esempio creando sulle pagine web un’area dove fossero presentate le news del sito, aggiornabili tramite un database. - 63 - CAPITOLO 3 In questo capitolo verrà descritta in modo accurato la realizzazione del progetto stesso, giustificando le eventuali scelte progettuali, in modo da far comprendere i vantaggi e gli svantaggi di alcune tipologie di decisioni. - 64 - 3.1 Analisi delle specifiche L’obiettivo principale di questo tirocinio è stato la realizzazione di un portale web, che facesse collimare la sicurezza con la semplicità di utilizzo. Prima di descrivere la fase realizzativa vera e propria si spiegheranno le motivazioni che hanno portato a certe scelte progettuali. Innanzitutto sono state esaminate le tecnologie disponibili per la realizzazione, in questo caso un server Microsoft in hosting con supporto database Microsoft Access e linguaggio ASP con supporto del protocollo http. Per quanto riguarda l’utilizzo di un database Microsoft Access, la scelta è stata avallata e non è stata richiesta una eventuale modifica con un DBMS (DataBase Management System) come MySQL o Oracle, in quanto il numero di dati da immettere e le query da effettuare in tale archivio risultano essere in numero esiguo, e quindi è adatto a tale scopo anche ad un database Microsoft Access senza precludere prestazioni sulla consultazione del portale. Visto che il portale sarà eseguito su di un server Microsoft e programmato in linguaggio ASP si è dovuto installare sul computer preposto allo sviluppo del progetto l’IIS (Internet Information Service) per poter collaudare in remoto le pagine sviluppate. Per quanto riguarda il fatto di utilizzare un protocollo insicuro come l’http, sono state fatte delle proposte di implementazione su protocollo https, ma visto il tempo ridotto del tirocinio, non è stato realizzato. A questo punto si può passare ad esaminare le specifiche richieste che possono essere riassunte in due punti: • accesso riservato con controllo condizionato degli accessi; • criptazione dei documenti depositati. Per esaminare queste specifiche si è proceduto con una prima fase di modellazione dei pericoli. 3.2 Modellazione dei pericoli La modellazione dei pericoli consente di individuarli in modo sistematico e di classificare quelli con la maggiore probabilità di incidenza sull’applicazione da sviluppare. Si può dire, senza suscitare grandi obiezioni, che non esiste un sistema sicuro al 100% quando un'applicazione web è esposta ad un ambiente ostile come Internet, quindi l'unica soluzione possibile per sviluppare un’applicazione web robusta è riconoscere la presenza di pericoli e ridurre o gestire i rischi associati. Anche se la modellazione dei pericoli risulta essere uno studio abbastanza elaborato e dispendioso in termini di tempo, e quindi di denaro, risulta essere - 65 - efficiente poiché rende la programmazione del software efficace già al primo sviluppo, basti pensare solo al fatto che risulta estremamente complicato identificare tutti i potenziali pericoli in una sola volta; in più la redazione della modellazione dei pericoli risulta un documento essenziale in fase di aggiornamento o di ampliamento del software. Il processo di modellazione dei pericoli può essere riassunto in sei fasi: Figura 22. Processo di modellazione dei pericoli Descriviamo in modo più preciso ogni fase. 1. Identificare i beni da proteggere: consiste nell’individuazione dei beni, quindi dei files o dei dati di valore che devono essere protetti dai sistemi ostili. 2. Creare una panoramica dell'architettura dell’applicazione: consiste nello sviluppo di diagrammi e tabelle semplici per documentare l'architettura dell'applicazione, inclusi sottosistemi, confini delle zone protette e flusso dei dati. 3. Scomporre l'applicazione: consiste nell’individuazione delle varie interconnessioni con i sistemi ostili in cui si interfaccia l’applicazione, per intercettare i vari punti vulnerabili del progetto. 4. Identificare i pericoli: consiste nell’individuazione dei pericoli che potrebbero interessare l'applicazione. 5. Documentare i pericoli: consiste nella redazione di un modello che descriva i vari pericoli a cui è vulnerabile l’applicazione. 6. Classificare i pericoli: consiste nella classificazione dei pericoli in modo da stabilire le priorità e poter affrontare prima quelli più seri, ovvero quelli che presentano i rischi maggiori. Il processo di classificazione valuta la probabilità del pericolo a fronte dei danni che potrebbe provocare in caso di attacco. È possibile che per alcuni pericoli sia preferibile non eseguire azioni correttive, se si paragona il rischio con i costi necessari per ridurlo. Iniziamo a redarre il modello dei pericoli. I beni da proteggere sono essenzialmente due: • credenziali di autenticazione immesse dai clienti e dai collaboratori; • file sensibili depositati dai clienti e dai collaboratori. - 66 - L’architettura dell’applicazione può essere schematizzata come in figura: Figura 23. Diagramma dell’architettura dell’applicazione Analizzando l’applicazione si vede che essa risulta interconessa con sistemi ostili in svariati punti. Possiamo suddividere i possibili pericoli per l’applicazione suddividendoli in: • pericoli a livello host; • pericoli a livello applicazione. I pericoli a livello host sono quelli che scaturiscono dalla configurazione del server Microsoft. Le vulnerabilità principali da considerare sono: • mancato aggiornamento con patch di sicurezza recenti, produce un alto rischio di esposizione a virus, cavalli di Troia, worms e attacchi IIS; • utilizzo di porte, protocolli e servizi non necessari, accrescono la superficie di attacco e consentono ad utenti malintenzionati di raccogliere informazioni sull'ambiente e di sfruttarle a proprio vantaggio; • possibilità di accesso anonimo non autenticato; • utilizzo di password e criteri account inadeguati, consentono attacchi di violazione delle password, spoofing dell'identità. Questa tipologia di pericoli non verrà esaminata in quanto essendo lo spazio fornito alla Phi.d'αlpha s.r.l. in hosting viene già gestito da tecnici specializzati e competenti. I pericoli a livello applicazione sono: • utilizzo di una convalida dell'input inadeguata, che apre la strada ad attacchi di script tra siti, SQL injection e di overflow del buffer; • passaggio di credenziali o cookie di autenticazione su collegamenti di rete non crittografati, che consente l'acquisizione di credenziali o il dirottamento di sessione; • utilizzo di criteri password e di account inadeguati, che consentono l'accesso non autorizzato; - 67 - • incapacità di proteggere gli aspetti di gestione della configurazione dell'applicazione, incluse le interfacce di amministrazione; • archiviazione di segreti di configurazione, come stringhe di connessione e credenziali degli account di servizio, in formato non crittografato; • utilizzo di account di processo e di servizio con privilegi eccessivi; • utilizzo di tecniche di codifica dell'accesso ai dati non sicure, che aumentano il pericolo associato agli attacchi di iniezione di comandi; • utilizzo di un sistema di crittografia debole o personalizzato e impossibilità di proteggere adeguatamente le chiavi di crittografia; • protezione basata sull'integrità dei parametri passati dal browser, ad esempio, i campi di modulo, le stringhe di query, i dati dei cookie, le sessioni e le intestazioni http; • utilizzo di una gestione delle eccezioni non sicura, che può aprire la strada ad attacchi di tipo Denial of Service e alla divulgazione di dettagli di livello sistema di grande utilità per i pirati informatici; • controllo e registrazione inadeguati, che possono comportare pericoli di ripudio. A questo punto si è passato alla classificazione del rischio di ogni pericolo. Il rischio può essere valutato come: rischio = probabilità ⋅ potenziale danno . Procedendo con questa metodologia si è verificato che i rischi a più alto valore sono: • uso insicuro della crittografia; • controlli di accesso insufficienti; • iniezioni di comandi; • gestione insicura delle sessioni; • gestione inadeguata degli errori. La fase di modellazione dei pericoli risulta conclusa, a questo punto si passa alla fase di valutazione delle possibili contromisure per limitare al massimo le vulnerabilità. 3.3 Analisi delle contromisure In questo paragrafo verranno descritte le possibili contromisure effettuabili per limitare l’effetto delle vulnerabilità a rischio più alto riscontrate nella fase di modellazione dei pericoli. - 68 - 3.3.1 Uso insicuro della crittografia L’utilizzo della crittografia verrà applicato ai file sensibili che i clienti ed i collaboratori trasmetteranno all’area riservata del portale web della Phi.d'αlpha s.r.l.. Sono possibili due strategie di inserimento della fase di de-cifratura, può essere implementata lato server o lato client. Per implementazione lato server si intende che la de-cifratura viene eseguita da una funzione ASP durante l’up-download del file come mostrato in figura: Figura 24. Diagramma dell’architettura dell’applicazione con cifratura lato server Per implementazione lato client si intende che la cifratura viene eseguita dagli utilizzatori nel proprio computer per mezzo di un software, dopodichè verrà eseguito l’upload del file già cifrato, mentre nella fase di download i file vengono prelevati senza procedure ASP direttamente dall’area riservata e decifrati nel proprio computer come mostrato in figura: Figura 25. Diagramma dell’architettura dell’applicazione con cifratura lato client L’implementazione scelta è stata quella lato client in quanto, anche se quella lato server risulta essere più facile da eseguire per l’utilizzatore, in quanto si trova ad - 69 - inserire solo la chiave ed il resto viene eseguito tutto dall’applicazione web, questo tipo di implementazione risulta inadeguata per due motivi, come prima cosa produce un’alta occupazione delle risorse del server con conseguente rallentamento, infatti essendo la fase di upload implementata in puro codice ASP e non con applicazioni installate sul server, inserire la fase di de-cifratura in questo punto provoca un grande tempo di trasmissione del file, come seconda cosa la trasmissione vera e propria viene eseguita in chiaro prima di essere cifrata dalla funzione ASP, quindi risulta vulnerabile ad attacchi di sniffing. In conclusione è stato sviluppato un software in VB.NET per gestire la decifratura dei file, non si è utilizzato software come PGP in quanto risultano essere troppo elaborati e di difficile comprensione per gli utilizzatori del portale. A questo punto sorge il problema di scelta fra un algoritmo asimmetrico o uno simmetrico. Visto che la Phi.d'αlpha s.r.l. ha esplicitamente richiesto che i file trasmessi potessero essere attribuiti al solo mittente e visto che l’utilizzo di procedure di autenticazione tramite smart card è stato scartato, visti i tempi ridotti del tirocinio, si è pensato di impiegare un algoritmo a chiave privata, nella fattispecie l’AES. Poiché gli algoritmi a chiave privata producono la gestione di un elevato numero di chiavi, si è pensato alla possibilità di sviluppo di un software steganografico che nascondesse dentro una immagine bitmap la chiave segreta, è stato quindi prodotto in VB.NET tale software. Si è pensato alla steganografia, poiché così la trasmissione delle chiavi può avvenire in modo sicuro anche tramite protocollo http ed è possibile per l’utente l’abbinamento del file trasmesso con la relativa immagine con chiave nascosta. 3.3.2 Controlli di accesso insufficienti Il portale deve gestire una fase di autenticazione degli utenti e proprio in questa fase che si insediano il maggior numero di vulnerabilità. La metodologia di autenticazione più utilizzata in ambito web si basa sull'invio delle proprie credenziali username e password da browser al server in chiaro, dopodichè, questo le confronterà con il database contenente le credenziali. Se non si utilizza un tunneling SSL, ciò può divenire un problema in quanto tramite un attacco di sniffing è possibile rintracciare le credenziali e conseguentemente riuscire ad ottenere l'accesso al programma web, utilizzando, appunto, le credenziali altrui così carpite effettuando un reply attack. Visto che il progetto sarà implementato su protocollo http si è pensato di cifrare la password con un algoritmo di hashing nella fattispecie MD5. Come per la fase di crittografia, l’implementazione della funzione di hash può essere eseguita lato server e lato client. Per implementazione lato server, si intende che la funzione di hash viene eseguita da uno script ASP durante la fase di autenticazione come mostrato in figura: - 70 - Figura 26. Diagramma dell’architettura dell’applicazione con hash lato server Per implementazione lato client si intende che la funzione di hash viene eseguita in fase di invio da uno script lato client, ad esempio utilizzando Java Script, come mostrato in figura: Figura 27. Diagramma dell’architettura dell’applicazione con hash lato client L’implementazione scelta è stata quella lato server in quanto, la gestione della funzione di hash lato client poteva produrre errori dovuti al blocco da parte del browser dello script Java Script ed il rischio di sniffing risultava essere molto limitato poiché, essendo il numero di utenti esiguo ed il loro accesso casuale, risultava molto difficile una possibile intercettazione di tali credenziali. Questo tipo di procedura di autenticazione risulta comunque vulnerabile ad attacchi di brute forcing, quindi si è predisposto l’inserimento di una password non intelligibile, per evitare attacchi a dizionario, e di minimo 8 caratteri alfanumerici. Un'altra vulnerabilità, di basso rischio, è dovuta alla funzione di hash, infatti sono possibili delle collisioni sulle password, ma per questo non è possibile fare nulla, se non utilizzare funzioni di hash robuste. - 71 - 3.3.3 Iniezioni di comandi Un altro punto focale da analizzare, sempre dovuto all’immissione delle credenziali di autenticazione, risulta essere quello di possibili attacchi dovuti ad iniezioni di comandi. Essendo il dialogo fra database e server gestito da codice SQL ci si troverà di fronte alla gestione di possibili attacchi di SQL injection. Per ovviare a questa vulnerabilità si è pensato di inserire una funzione di validazione dei dati che sostituisca i caratteri “sospetti” con caratteri non nocivi in modo da sventare l’attacco. 3.3.4 Gestione insicura delle sessioni Per quanto riguarda la gestione delle sessioni create per tenere traccia degli utenti autenticati nell’area riservata la soluzione ottimale sarebbe quella di utilizzare il protocollo https, ma visto che non è possibile, si sono usati degli accorgimenti che ne riducono le vulnerabilità, come ad esempio il fatto che i dati di sessione vengono inviati esclusivamente tramite POST e mai con il metodo GET poiché quest’ultimo risulta visibile nell’url. Per evitare che le pagine dell’area riservata fossero richiamate ed accessibili in modo automatico attraverso l’inserimento dell’url o tramite ricerca su motore di ricerca si è implementata una funzione di verifica tramite le variabile di sessioni che se fornisce esito negativo, quindi l’utente non è loggato, lo redirige sulla pagina di login. Per quanto riguarda la fase di logout si è prestata molta attenzione al fatto di possibilità di raggiungere le pagine dell’area riservata per mezzo del bottone indietro del browser, per ovviare a questa vulnerabilità si è sviluppata una funzione che valuta l’expires della pagina attraverso una procedura condizionale. 3.3.5 Gestione inadeguata degli errori Le ultime vulnerabilità prese in esame sono state quelle dovute alla gestione superficiale degli errori. La gestione corretta degli error fa si che un errore dell’applicazione web, non sia fonte di informazioni sulla struttura interna del software ad eventuali attacchi. Basti notare che anche il semplice File Not Found piuttosto di un Access Denied può essere un’informazione utile per un utente malintenzionato. Un altro esempio sono gli stack trace che generati dalla Virtual Machine forniscono spezzoni di codice a chi richiede la pagina al cui interno risiede un errore di programmazione. Per eliminare questa tipologia di vulnerabilità è stata effettuata una revisione completa del codice sorgente ed è stata effettuata una gestione accurata delle eccezioni in modo tale che restituiscano messaggi chiari per l’utente e non compromettenti per l’applicazione web. - 72 - 3.4 Implementazione del software lato client Per lo sviluppo del software lato client è stato utilizzato come linguaggio di programmazione il Visual Basic .NET, e come piattaforma IDE il Microsoft Visual Basic 2005 Express Edition. Si è scelto di utilizzare questa versione del compilatore poiché risulta essere totalmente gratuita e scaricabile dal sito Microsoft. Anche se presenta alcune limitazioni, come ad esempio la modalità di debug delle applicazioni solo in locale e la bassa estendibilità all’aggiunta di strumenti esterni al menù e all’uso di controlli di terze parti, risulta essere più che sufficiente per lo sviluppo del software relativo alla gestione delle policy di sicurezza richieste. Altro punto a favore, è che a differenza delle versioni Standard e Professional, risulta essere molto compatto, circa 40 MB, e la Microsoft non ha imposto nessun vincolo sulla creazione di software commerciale. I software che verranno sviluppati sono due: • software per la gestione della crittografia; • software per la gestione della steganografia. 3.4.1 Software per la gestione della crittografia Per lo sviluppo del software di crittografia si è utilizzata la classe appartenente al namespace System.Security.Cryptography in cui il Framework mette a disposizione del programmatore un insieme di classi dedicate a quasi ogni tipo di algoritmo di cifratura, sia per funzioni di hashing sia per algoritmi simmetrici sia asimmetrici. Come detto in precedenza si è scelto di utilizzare l’algoritmo di cifratura AES poiché fornisce un elevata robustezza. Il software, tralasciando la gestione degli errori e delle eccezioni, può essere schematizzato attraverso due schemi a blocchi, uno per la fase di cifratura l’altro per la fase di decifratura: Figura 28. Schema a blocchi delle funzioni di de-cifratura - 73 - La lettura e scrittura del file avvengono come strem binari poichè si è voluto creare un’applicazione che gestisse qualsiasi tipo di file, se si fosse voluto ad esempio gestire solo file di testo si sarebbe potuto utilizzare una codifica come ad esempio la UTF-8. Le funzioni più importanti di questo programma sono quella di cifratura e quella di decifratura. Su entrambe le funzione devono essere inizializzati i valori di alcune variabili indispensabili per il funzionamento della classe che implementa l’AES. Come prima cosa è necessario definire: • BlockSize: dimensione del blocco; • IV: vettore di inizializzazione. La BlockSize è l'unità minima da crittografare e gli viene assegnato il valore di 16 byte. Il vettore di inizializzazione ha un significato particolare che deve essere spiegato. L’algoritmo AES per crittografare usa la chiave segreta ed il risultato della codifica dei byte precedenti, quindi è chiaro che prima dei primi 8 byte non ci sarà nulla, quindi è necessario fornire una base di partenza all’algoritmo e questa è proprio il vettore di inizializzazione. Anche il vettore di inizializzazione ha la dimensione di 16 byte. A questo punto è necessario fornire la chiave segreta con la sua relativa dimensione. La chiave segreta viene data dall’utilizzatore del software attraverso la compilazione di una TextBox, quindi assumerà il tipo stringa. Essendo un valore immesso, deve prima essere validato, per fare ciò è stata creata una funzione apposita che attraverso un’espressione regolare controlla che siano immessi solo caratteri alfanumerici (alfabetici minuscolo, alfabetici maiuscolo, numeri) e che essi siano in numero sufficiente rispetto alla dimensione della chiave scelta, infatti è stata implementa la possibilità di far scegliere all’utilizzatore la lunghezza della chiave fra 128 bit (16 caratteri) e 256 bit (32 caratteri). Il fatto di aver limitato il numero delle possibili chiavi risulta un indebolimento alla robustezza dell’algoritmo, infatti si è passati dalla possibilità di inserire 256 caratteri a quella di inserirne 62, nella fattispecie per le chiave a 128 bit si passa da un valore di 2128 ≅ 3, 4 ⋅1038 ad un valore di 6216 ≅ 4,8 ⋅1028 mentre per le chiavi a 256 bit si passa da un valore di 2256 ≅ 1, 2 ⋅1077 ad un valore di 6232 ≅ 2, 3 ⋅1057 . Questa scelta è stata effettuata poiché risulta più semplice per l’utilizzatore inserire una stringa di caratteri alfanumerici piuttosto che una sequenza di codice binario o esadecimale qualsivoglia. Una volta impostate queste proprietà della classe è possibile effettuare la decifratura utilizzando i metodi : • CreateEncryptor().TransformFinalBlock( , ,); • CreateDecryptor().TransformFinalBlock( , ,); - 74 - dove la funzione TransformFinalBlock accetta come parametri: • inputBuffer: è l’input sul quale eseguire l'operazione; • inputOffset: è l’offset nella matrice di byte dal quale iniziare a utilizzare i dati; • inputCount: è il numero di byte nella matrice di byte da utilizzare come dati; e restituisce l’array di byte contenente il file de-cifrato. Come richiesto dalla Phi.d'αlpha s.r.l. si è cercato di sviluppare il software in modo tale che fosse il più friendly possibile. Il software si presenta come una sola finestra in cui è possibile effettuare tutte le operazioni. Figura 29. Finestra del software per la gestione della crittografia Per rendere il più semplice possibile l’utilizzo del software è stata gestita l’immissione del file da de-cifrare per mezzo di un’operazione di Drag&Drop, infatti per ottenere il path del file basta trascinarlo nella TextBox denominata “Path”. A questo punto l’utilizzatore sceglierà la lunghezza della chiave scegliendo fra 16 e 32 caratteri ed inserirà la chiave nella TextBox denominata “Chiave” infine sceglierà se effettuare la cifratura o la decifratura premendo i bottoni in basso a destra ed in basso a sinistra. Il file cifrato avrà come estensione .cry mentre il file decifrato ritornerà ad avere l’estensione originale. I files prodotti dal software verranno posizionati nella stessa directory del file che lo ha generato. Per evitare difficoltà dovute ad errori dell’utilizzatore sono state gestite tutte le possibili eccezioni con il relativo messaggio di spiegazione sull’errore commesso, sono state quindi gestiti errori dovuti a path non validi, a chiavi segrete non valide, etc. 3.4.2 Software per la gestione della steganografia Per ottenere una trasmissione della chiave maggiormente sicura si è pensato di inviarla in un file steganografato. - 75 - Per rendere il file meno vulnerabile ad un possibile attacco, invece di utilizzare un software in commercio ne è stato sviluppato uno apposito, con un algoritmo ottimizzato. La tecnica steganografica applicata è di tipo sostitutivo e vengono utilizzati come file contenitori, immagini Bitmap. Ipotizzando una distribuzione equiprobabile fra gli zero e gli uno del file e quelli del messaggio, andando a modificare i bits meno significativi del file contenitore si produrrebbe una variazione del 50% dei byte che devono contenere il messaggio segreto. Per diminuire questa probabilità si è pensato, che essendo il messaggio relativamente corto in quanto la chiave può essere di 16 o 32 caratteri, è possibile utilizzare 64 byte per ogni carattere poiché i caratteri alfanumerici sono 62, richiedendo file contenitori di minimo 3 kB per chiavi da 16 caratteri e 6 kB per quelle da 32 caratteri. I 64 byte possono essere immaginati, pensando alla struttura di un file Bitmap, come una matrice 8 ⋅ 8 come in figura: Figura 30. Matrice contenente la codifica dei caratteri della chiave dove solo il riquadro nero identifica il carattere. Essendo i riquadri bianchi molto più numerosi di quelli neri si è pensato di associare a loro una sequenza di bits meno probabile. In particolare i riquadri neri sono stati rappresentati con una sequenza di soli zero mentre quelli bianchi con qualsiasi altra sequenza purché sia presente almeno un uno. 1 La probabilità composta di avere n bits zero consecutivi è n , quindi la 2 1 probabilità di avere tutte le altre parole sarà 1 − n . 2 Immaginando di utilizzare questo procedimento su di un file contenitore con distribuzione equiprobabile di zero ed uno si vedrà che aumentando il numero di bits costituenti i riquadri il numero di bits da sostituire tenderà a diminuire. - 76 - Ad esempio, per un blocco di 1024 byte si avrà che: N. di bits costituenti il riquadro 1 2 3 4 5 6 N. di bits da modificare nel file contenitore 512 264 144 88 64 56 Tabella 10. Esempio di bits da modificare nel file contenitore in base ai bit costituenti il riquadro Uno dei casi peggiori che si può avere, si presenta quando si usa come file contenitore un’immagine completamente bianca. Si è per esempio utilizzata un’immagine 11 ⋅ 32 di 1056 byte ed è stato modificato sempre il primo riquadro dei 64. In questo caso anche un semplice attacco visuale fa notare l’esistenza di una anomalia nell’immagine ed andando a scomporla nei canali RGB è addirittura possibile identificare una certa sequenzialità su bits: Figura 31. Analisi visiva fra un immagine e la sua relativa steganografata Utilizzando un attacco statistico, l’anomalia risulta evidente, in quanto esaminando l’immagine convertita in cifre esadecimali, si nota subito l’improbabilità di un rumore strutturato come quello in figura. - 77 - Figura 32. Analisi statistica fra un immagine e la sua relativa steganografata Utilizzando invece immagini con variazioni di colore molto frequenti il risultato è molto apprezzabile, in quanto ad un attacco visivo risulta essere indistinguibile, mentre ad un attacco statistico, utilizzando sempre immagini diverse si rende difficoltosa l’analisi. Per giustificare quanto detto verrà fornito un esempio con immagine cover con alta varianza nella frequenza di colore. Figura 33a. Immagine originale Figura 33b. Immagine steganografata L’immagine ha una dimensione di 1600 ⋅1200 di 5760054 byte. Per la steganografia dell’immagine sono stati utilizzati i seguenti parametri: • chiave segreta: 534b44a19bf18d20b71ecc4eb77c572f • numero i bit per byte: 2 • shift del cifrario a scorrimento: 23 ottenendo una variazione di 532 bit su 523 byte, cioè una media di 1,02 bit per byte. - 78 - Esaminando l’immagine convertita in cifre esadecimali si vede come l’immagine steganografata abbia una piccola variazione sulle componenti RGB del colore quasi sempre nell’ordine dell’unità. Figura 34. Frammento di codice esadecimale dell’immagine e della sua relativa steganografata Per rendere più sicuro l’algoritmo, i codici che costituiscono la chiave possono essere cifrati per mezzo di un cifrario a scorrimento. Il software, tralasciando la gestione degli errori e delle eccezioni, può essere schematizzato attraverso due schemi a blocchi, uno per la fase di inserimento della chiave e l’altro per la fase di estrapolazione: Figura 35. Schema a blocchi delle funzioni di iniezione ed estrapolazione della chiave Il software si presenta come una sola finestra in cui è possibile effettuare tutte le operazioni. Figura 36. Finestra del software per la gestione della steganografia - 79 - Come per il software per la crittografia, l’immissione del file è stata gestita per mezzo di un’operazione di Drag&Drop. Anche in questo caso l’utilizzatore può scegliere la lunghezza della chiave scegliendo fra 16 e 32 caratteri ed inserirla nella TextBox denominata “Chiave”. L’utilizzatore può anche scegliere il numero di bit da modificare per ogni byte dell’immagine contenitore, in quanto come descritto aumentando tale numero, in linea teorica si diminuisce la variazione sull’immagine steganografata. Tale numero è stato settato di default a 3 in quanto si è verificato che è un valore accettabile per qualsiasi tipo di combinazione immagine contenitore - chiave. Per effettuare l’iniezione della chiave o il prelevamento della chiave, l’utilizzatore sceglierà premendo i bottoni “Nascondi la chiave” e “Preleva la chiave”. Il file Bitmap steganografato verrà posizionato nella stessa directory del file che lo ha generato. Per evitare difficoltà dovute ad errori dell’utilizzatore sono state gestite tutte le possibili eccezioni con il relativo messaggio di spiegazione sull’errore commesso, sono state quindi gestiti errori dovuti a path non validi, è stato quindi gestito l’input forzato di soli file Bitmap di dimensioni minime opportune, a chiavi segrete non valide, etc. Nella sezione di iniezione è stato implementato anche un campo denominato “Utilizzo bit”, dove viene data una stima dell’efficienza dell’algoritmo in base alla immagine cover ed alla chiave utilizzata. Un utilizzatore attento può pensare di fare alcuni tentativi modificando la cifratura a scorrimento, il numero di bit da modificare per ogni byte dell’immagine contenitore, ed eventualmente l’immagine cover stessa per ottenere il risultato migliore possibile. Per migliorare l’algoritmo si potrebbe pensare di codificare i riquadri neri seguendo una sequenza pseudo-random ed utilizzando un algoritmo di cifratura più efficiente. 3.5 Implementazione dell’applicazione web Il software lato server è stato sviluppato utilizzando come linguaggi di programmazione: Visual Basic Script, Java Script, ASP ed il linguaggio di markup HTML. Visto che il portale sarà eseguito su di un server Microsoft si è installato sul computer preposto allo sviluppo del progetto l’IIS (Internet Information Service) per poter collaudare in remoto le pagine sviluppate. Essenzialmente l’applicazione web è costituita da due parti. La prima è costituita da un fase di autenticazione per filtrare gli utenti preposti all’ingresso nell’area riservata della Phi.d'αlpha s.r.l. ed una connessa fase di logout in cui gli utenti possono uscire dall’area riservata in modo sicuro. - 80 - La seconda parte è costituita da una sezione in cui gli utenti possono effettuare l’upload dei file che desiderano condividere con annessa una sezione in cui è possibile eseguire il download di tutti i file inseriti. 3.5.1 Login e logout degli utenti Per sviluppare la fase di login, cioè quella in cui gli utenti vengono autenticati, si è proceduto inizialmente alla creazione di un database Microsoft Access dove potessero essere contenute tutte le credenziali degli utenti. Come prima cosa si è creato un nuovo database a cui è stato dato il nome “user.mdb” ed è stata creata una nuova tabella dal nome “Utenti” con i seguenti campi: • Id; • Username; • Password. La tabella in visualizzazione struttura risulta costituita come in figura: Figura 37. Visualizzazione struttura della tabella “Utenti” A questo punto è stata costruita una pagina in cui fosse possibile per l’utente inserire le proprie credenziali. Si è così implementata una form rientrante in cui tramite il metodo POST vengono validate le credenziali. La form è costituita da un campo UserName e da un campo Password. Figura 38. Form per il login L’UserName viene inviato in chiaro mentre la Password viene cifrata per mezzo di una funzione di hash, nella fattispecie utilizzando l’algoritmo MD5. Non essendo disponibile una tale funzione nel linguaggio ASP, si è proceduto all’inserimento della stessa utilizzandone una implementata da Phil Fresle e prelevata dal sito www.frez.co.uk. Tale funzione è stata sviluppata, come illustrato dall’autore, utilizzando le specifiche fornite dalla RSA Laboratories e - 81 - descritte nel documento RFC1321 ed è stata programmata in linguaggio ASP e Visual Basic Script. La funzione chiamata “MD5” accetta come input una stringa e restituisce una stringa. Le credenziali, immesse nella form, prima di essere confrontate con quelle contenute nel database vengono validate per scongiurare possibili attacchi di iniezione di comandi. Avendo usato come linguaggio, per generare le query, l’SQL si è creata una funzione ad-hoc atta a filtrare possibili attacchi di SQL injection. La funzione chiamata “FiltraSQL” accetta come input una stringa e restituisce una stringa. Tale funzione non fa altro che verificare se la stringa immessa nella form contiene caratteri preposti ad attacchi di SQL injection e se ne trova essi vengono sostituiti con caratteri non nocivi. Risulta essenziale scongiurare questo tipo di attacco perché è impiegabile anche da hacker poco espserti in quanto basta ad esempio inserire come UserName il nome dell’amministratore del servizio (ad esempio “root”) e come password il codice: “ ' or '0' = '0 ” per ottenere l’acceso all’area riservata poiché la query utilizzata per il confronto delle credenziali avrà il seguente codice: select * from Utenti where UserName = 'root' and Password = ' ' or '0 ' = '0 ' Siccome il carattere ' viene interpretato dal linguaggio ASP come il carattere di fine stringa la where sarà sempre verificata. Una volta effettuati i controlli sui dati immessi, è possibile effettuare la validazione andando a compiere il confronto fra le credenziali immesse nella form e quelle contenute nel database, se il confronto da un esito positivo allora vengono create due sessioni chiamate “Log” e “Username” dove alla prima viene assegnato il valore True e alla seconda viene assegnato l’UserName dell’utente; se invece il confronto da un esito negativo viene assegnata alla sessione “Log” il valore False. Se il confronto è positivo l’utente viene indirizzato all’area ad accesso riservato, mentre se il confronto risulta essere negativo l’utente viene reindirizzato di nuovo alla pagina di login a cui viene aggiunto un messaggio per comunicargli che i dati immessi non sono validi. Oltre ai controlli descritti vengono eseguite anche tutte le procedure atte a garantire eccezioni ed errori, come ad esempio la mancata compilazione di un campo della form, in modo da rendere il più semplice possibile la verifica da parte dell’utilizzatore dell’errore da lui commesso. Per garantire che le pagine web dell’area riservata siano accessibili solo dagli utenti autorizzati oppure non siano generate in modo automatico, sia dall’immissione dell’url o da risultati di ricerche ottenute dai motori di ricerca, in ogni pagina viene controllato se l’utente che tenta di accedere è regolarmente - 82 - loggato, viene quindi effettuato un controllo sulle due variabili si sessione “Log” e “Username”. Se l’utente non risulta autorizzato viene reindirizzato alla pagina di login. La fase di logout viene eseguita tramite due procedure: • Session.Contents.RemoveAll(); • Session.Abandon. Con la prima procedura vengono cancellate tutte le variabili di sessione, cioè vengono svuotate e annullate, quindi non esisteranno più nel corso della navigazione dell’utete, mentre con la seconda procedura si cancellano tutte quante le variabili di sessione attive nell’istante in cui il comando viene inoltrato. Una volta eseguito il logout non dovrebbe più essere possibile ritornare all’area riservata invece, se non viene preso un adeguato accorgimento, tramite l’utilizzo del bottone “Indietro” del browser è possibile. Per evitare questa anomalia, si è sviluppata una funzione, che come prima cosa fa in modo tale che le pagine non rimangano nella cache del browser, e poi tramite una procedura condizionale valuta se la pagina viene visitata tramite un accesso dall’area riservata o tramite un accesso successivo alla fase del logout, nel primo caso consente l’accesso, mentre nel secondo caso redirige l’utilizzatore alla pagina di login. Per concludere si vuole spiegare perché si è scelto di utilizzare le sessioni. Le sessioni provocano, se in numero elevato, un notevole appesantimento di lavoro sul server rallentandolo di conseguenza, infatti queste variabili lavorano completamente sul Server-Side. Per ovviare a questo inconveniente si potrebbe pensare di utilizzare delle variabili Client-Side, come ad esempio i cookies. Non si è scelto di utilizzare i cookies, poiché essi potrebbero in alcuni casi essere stati disabilitati dagli utenti e questo porterebbe ad un non funzionamento dell’interfaccia di autenticazione, mentre le sessioni si appoggiano a dei cookies speciali generati dal server e sono quindi sempre attive. Come ultima ragione che ha portato alla scelta delle sessioni c’è quella relativa al fatto che esse sono in numero bassissimo e quindi non provocano un lavoro stressante per il server. 3.5.2 Upload e download dei files La Phi.d'αlpha s.r.l., nelle specifiche fornite, ha richiesto un’area in cui fosse possibile inserire e prelevare del materiale informatizzato tramite operazioni di criptazione. Le procedure di criptazione sono state ottenute, come descritto precedentemente, attraverso un software lato client, quindi rimane da progettare un’applicazione web in grado di gestire l’upload ed il download dei files. Nel linguaggio ASP non esiste una funzione predisposta ad effettuare l’upload dei files, quindi normalmente per implementare tale funzionalità ci si deve servire di componenti esterni che vengono eseguiti sul server. Purtroppo però questi - 83 - componenti, oltre che ad avere un costo elevato, necessitano di un'installazione da parte della società di hosting, cosa che non sempre viene effettuata per motivi di sicurezza. Un'altra ragione per cui non si è scelto di utilizzare queste tecnologie è che esse non sono modificabili, poiché raramente sono open source, quindi spesso non sono ottimizzate per le specifiche di chi le vuole utilizzare. Visto che il protocollo http supporta l'upload ed anche il Web Server di Microsoft IIS, si è pensato di sviluppare un’applicazione web in grado di implementare l’upload dei files attraverso puro codice ASP. Come prima cosa deve essere creata una pagina in grado di recuperare i dati e spedirli. Per fare questo ci si è serviti di una form. Figura 39. Form per l’upload Analizzando la documentazione ufficiale della Microsoft si nota che l'unico metodo per accedere al contenuto del file uploadato è richiamare il metodo RequestBinary: bytecount = Request.TotalBytes bytArray = Request.BinaryRead(bytecount) dove bytecount è una variabile long che restituisce la lunghezza della richiesta, mentre bytArray è un array che contiene i byte della richiesta. In pratica il metodo BinaryRead legge un certo numero di byte direttamente dal corpo della richiesta http inviato dal client come parte del metodo POST. Dovendo leggere l'intero file bisognerà conteggiare quanti byte sono stati spediti e memorizzarli in una variabile che sarà quindi il file da uploadare. Putroppo il linguaggio Visual Basic Script non dispone di funzioni in grado di convertire, in modo automatico, tutto un Array da byte a char (string), quindi si dovrebbe costruire un ciclo su ogni elemento dell'array per creare una stringa di caratteri in questo modo: For lngLoop = 0 To UBound(bytArray) - 1 strByteToString = strByteToString + Chr(bytArray(lngLoop)) Next Questo ciclo rallenta molto il processo di uploading, ed in alcuni casi si possono verificare dei timeout che ne bloccano addirittura l’esecuzione. Per evitare questo si può pensare di utilizzare la libreria ADODB che attraverso il metodo "AppendChunk" dell'oggetto Field, permette di processare i dati come un field di tipo long binary, in modo da restituire una stringa. - 84 - Utilizzando la libreria ADODB il ciclo precedente viene sostituito dal seguente codice: Set rstTemp = Server.CreateObject("ADODB.Recordset") rstTemp.Fields.Append "bytArray", adLongVarChar, lenb(bytArray) rstTemp.Open rstTemp.AddNew rstTemp.Fields("bytArray").AppendChunk bytArray rstTemp.Update strByteToString = rstTemp("bytArray") che fa si che nella variabile stringa strByteToString sia contenuta la richiesta serializzata. A questo punto la richiesta è manipolabile con i metodi e le funzioni generalmente utilizzate, con Visual Basic Script, quali Mid e InStr. Un esempio di richiesta serializzata è il seguente: -----------------------------7d1193202027a Content-Disposition: form-data; name="uploadFile"; filename="C:\Esempio.cry" Content-Type: text/plain ·"Eê [1] &möüy|Ã"óoÊYE?lÄïV?$c [¿~åÿ1êòbà ƒÃ>^"*-|D¡ïfÃŒ|é÷"Eê [1] &möüy|Ã"óoÊYE?lÄÃd Figura 40. Frammento di richiesta serializzata in cui: • 7d1193202027a: è il codice identificativo del file spedito; • Content-Disposition: indica la provenienza del file, in questo caso una form; • name: contiene il nome del campo form, in questo caso “uploadFile”; • filename: contiene il percorso ed il nome del file spedito, in questo caso “C:\Esempio.cry”; • Content-Type: contiene la tipologia del file spedito, in questo caso text/plain; ed il resto è il contenuto del file che viene chiuso attraverso un tag apposito. Il codice identificativo si ripete per ogni richiesta serializzata, può quindi essere utilizzato per effettuare un ciclo. In ogni ciclo si recuperano: nome dell'input, tramite la stringa: "name" ed il suo relativo contenuto. Quando il campo è di tipo file, nella richiesta serializzata si troverà anche l'attributo "filename" con il percorso da cui è stato prelevato il file. A questo punto bisogna recuperare il contenuto del file dalla richiesta stessa. Per fare ciò si è utilizzata una classe scritta da Simone Medas con licenza BSDlike prelevata dal sito www.aspitalia.com. - 85 - Tale classe chiamata “clsInput” ha come membri: • name: variabile stringa che identifica il nome assegnato all'input; • isFile: variabile booleana, vale "true" se è un file; • size: variabile numerica, contiene la lunghezza del file contenuto; • fileName: variabile stringa che contiene il nome del file uploadato; • filePath: variabile stringa che contiene il percorso completo dal quale è stato uploadato il file; e due metodi: • saveFile(): salva il file dove è in esecuzione lo script • saveFileAs(destinationPath, newFileName): permette di specificare sia il percorso di destinazione (nel server) che un eventuale ed opzionale nome da assegnare al file. Il parser si occupa di creare una collezione di clsInput utilizzando l'oggetto "Scripting.Dictionary", dove l'oggetto in questione è "inputs". Bisogna infine specificare che questa classe, è utilizzabile solo nella versione 5.0 di Visual Basic Script, quindi con IIS5 o IE 5.x o con l'installazione della VBScript Virtual Mchine 5.0. Ora si sono gestite tutte le eccezioni, cioè quella dovuta alla mancata scelta di un file e quella dovuta alla scelta di un file il cui nome è già presente nell’area riservata. Per quanto riguarda la prima si è fatto in modo che sia visualizzato un messaggio che esplichi l’avvenuta eccezione all’utilizzatore, mentre per la seconda, in accordo con la Phi.d'αlpha s.r.l., si è fatto in modo tale che l’utilizzatore non possa effettuare sovrascrzione dei files. Quando l’utente sceglie il file da inviare e preme il bottone invia, viene mandato al server la richiesta, a questo punto tramite l’utilizzo dell’oggetto FileSystemObject si fa una scansione di tutti i file contenuti nell’area riservata e se uno dei nome dei file risulta essere uguale a quello che cerca di mandare l’utente, viene bloccato l’upload e generato un messaggio di errore nel quale viene detto che esiste già un file con quel nome e che non è possibile sovrascriverlo, e che se si vuole comunque mandarlo bisogna cambiargli il nome. Per quanto riguarda la pagina in cui è possibile effettuare il download dei files condivisi, essa viene generata in modo automatico attraverso uno script ASP. Tramite l’oggetto FileSystemObject viene eseguita una scansione dell’area riservata per catalogare tutti i files in essa contenuti, e viene creata una tabella in cui vengono elencati: • nome del file; • tipo del file; • dimensione del file. Nel campo tipo del file è stato effettuato un controllo per vedere se il file ha estensione .cry in modo da evidenziare che si tratta di un file crittografato attraverso il software sviluppato. I files contenuti nell’area riservata potranno a discrezione della Phi.d'αlpha s.r.l. essere cancellati attraverso un accesso Ftp a tale cartella. - 86 - CONCLUSIONI E SVILUPPI FUTURI Durante il tirocinio effettuato presso la Phi.d'αlpha s.r.l. si è cercato di implementare un portale web sicuro ed allo stesso tempo estremamente friendly. Il tema della sicurezza risulta essere al giorno d’oggi estremamente sentito per tutte quelle aziende che si trovano a lavorare con dati riservati e sensibili, poiché una loro trattazione in modo non corretta può provocare danni irreparabili sia per i clienti, sia per l’azienda stessa che si potrebbe trovare a fronteggiare elevate sanzioni pecuniarie o addirittura procedimenti di natura penale. Visto ciò risulta estremamente difficile far collimare la semplicità con la sicurezza. Le procedure attuate per la realizzazione di questo progetto risultano essere abbastanza sicure, ma non esenti da possibili attacchi. Purtroppo sviluppare procedure per garantire policy di sicurezza più elevate producono una diminuzione della semplicità di utilizzo del software e questo in alcuni casi, può provocare danni ancora più irreparabili in quanto un utente “non addetto ai lavori” si troverebbe ad utilizzare strumenti a lui incomprensibili e quindi potenzialmente dannosi. Nonostante ciò si può dire che le garanzie di sicurezza implementate risultano essere sufficienti per la Phi.d'αlpha s.r.l., in quanto essendo un’azienda non molto grande, si trova a prestare i propri servizi ad un numero ristretto di clienti ed a collaborare con un altrettanto numero limitato di collaboratori, quindi ci si è potuti limitare a trattare solo quelle vulnerabilità che sono di importanza notevolmente rilevante. Se si vuole, però analizzare dei possibili sviluppi futuri, il lavoro da fare risulta essere ragguardevole. Come prima cosa, sarebbe necessario implementare tutta la sezione dell’area riservata del portale sotto protocollo https, in modo da garantire un livello più elevato di sicurezza a possibili attacchi di sniffing. Per quanto riguarda invece l’utilizzo della crittografia e della steganografia, sarebbe necessario poter effettuare degli incontri, per poter istruire tutti i fruitori di queste tecnologie spiegando come tali algoritmi se usati male, non garantiscono policy di sicurezza elevate, basti pensare ad esempio all’uso scorretto delle chiavi segrete. Per quanto riguarda invece proprio il software prodotto, per quello realizzato per implementare l’algoritmo di cifratura AES ci si può ritenere soddisfatti, mentre per quello che riguarda il software che implementa la steganografia sarebbe meglio apportare alcune modifiche all’algoritmo che realizza l’iniezione della chiave nel file, in quanto, esso risulta segreto fintanto che un hacker non viene a conoscenza dell’algoritmo stesso, e questo non rispetta il postulato di Kerckhoffs. Per rendere l’algoritmo di steganografia veramente sicuro bisognerebbe utilizzare una tecnica di memorizzazione dei vari frammenti della chiave pseudo-casuale e che essa sia preventivamente cifrata. - 87 - L’applicazione web risulta essere estremamente friendly e quindi di facile utilizzo anche a tutti quegli utenti non esperti del settore informatico, in quanto richiede solo l’immissione delle credenziali di autenticazione, fornite dalla Phi.d'αlpha s.r.l. e quindi l’utilizzatore non deve compilare tutti quelle form preposte all’accettazione o al cambiamento delle credenziali, che spesso scoraggiano quella tipologia di utenti poco esperti. Un ultima opinione si può esprimere sulla modalità di cancellazione dei files dal server che la Phi.d'αlpha s.r.l. compie. Essa non fornisce nessuna garanzia di sicurezza in quanto la cancellazione “normale” di un file non ne realizza l’effettiva eliminazione dalla memoria, quindi sarebbe indicato realizzare un software che riuscisse, per esempio attraverso sovrascrizioni multiple delle locazioni di memoria che contenevano il file, ad eliminare in modo definitivo il file. In conclusione si può dire che la realizzazione di questo progetto è stata molto interessante, poiché oltre a fornire diversi punti di studio su argomenti estremamente degni di attenzione come la crittografia e la steganografia, ha fornito una fondamentale esperienza sulla vita aziendale e sulle correlate esigenze pratiche che risultano molto spesso distanti da quelle di natura teorica. - 88 - APPENDICE In questa sezione verrà inserito il codice sorgente del progetto realizzato suddividendolo in: • codice VB.NET del software di crittografia; • codice VB.NET del software di steganografia; • codice ASP del software web. - 89 - Codice VB.NET del software di crittografia Option Explicit On Imports System.Security.Cryptography Imports System.IO Imports System.Text Imports System.Text.RegularExpressions Public Class frmMain 'dichiaro le variabili pubbliche Public bytIV() As Byte = {123, 245, 166, 166, 135, 73, 13, 39, 255, 91, 45, 78, 14, 211, 22, 62} Public strPath As String 'contiene il Path del file Public abytFile As Byte() 'contiene il file in byte Public blnFlag As Boolean 'contiene il flag per l'inserimento del nome Public strEstensioneFile As String 'contiene l'estensione del file Public intDimensioneChiave As Integer 'contiene la dimensione della chiave #Region "Drag&Drop" 'restituisce il Path del file trascinato in strPath Private Sub txtPath_DragDrop(ByVal sender As Object, _ ByVal e As System.Windows.Forms.DragEventArgs) Handles txtPath.DragDrop If (e.Data.GetDataPresent(DataFormats.FileDrop)) Then e.Effect = DragDropEffects.Copy Else e.Effect = DragDropEffects.None End If End Sub Private Sub txtPath_DragEnter(ByVal sender As Object, _ ByVal e As System.Windows.Forms.DragEventArgs) Handles txtPath.DragEnter Dim sPathName As String() sPathName = e.Data.GetData(DataFormats.FileDrop) txtPath.Text = sPathName(0) strPath = sPathName(0) txtChiavescelta.Text = "" End Sub #End Region #Region "Funzioni per Lettura e Scrittura" Private Function LeggiFile(ByVal vstrPathName As String) As Byte() 'leggo il file usando BinaryReader in modo da ottenere un'array di byte Dim objFileL As New FileStream(vstrPathName, FileMode.OpenOrCreate, FileAccess.Read) Dim objFileLBR As New BinaryReader(objFileL) abytFile = objFileLBR.ReadBytes(objFileL.Length) objFileL.Close() objFileLBR.Close() Return abytFile End Function - 90 - Private Sub ScriviFile(ByVal vstrPathName As String, ByVal vabytFileS As Byte()) 'sfruttando la classe Path ottengo: directory e nome del file Dim strDirFile As String = Path.GetDirectoryName(vstrPathName) Dim strNomeFile As String = Path.GetFileNameWithoutExtension(vstrPathName) 'in base al tipo di operazione: cripto o decripto modifico l'estensione del file Dim strEstFile As String If blnFlag = 0 Then strEstFile = ".cry" Else strEstFile = "." & strEstensioneFile End If 'se il file esiste già aggiungo al nome un trattino If File.Exists(strDirFile & "\" & strNomeFile & strEstFile) Then strNomeFile &= "_" End If Dim objFileS As New FileStream(strDirFile & "\" & strNomeFile & strEstFile, FileMode.Create, _ FileAccess.Write) Dim objFileSBW As New BinaryWriter(objFileS) objFileSBW.Write(vabytFileS) objFileS.Close() objFileSBW.Close() End Sub #End Region Public Function Cripta(ByVal vstrIV As Byte(), ByVal vstrChiave As String, _ ByVal vabytFileC As Byte()) As Byte() 'array di byte che contiene il file criptato Dim abytFileC As Byte() 'inizializzo la classe RijndaelManaged Dim objAES As RijndaelManaged = New RijndaelManaged 'dimensione della chiave: 128 bit = 16 byte, 256 bit = 32 byte objAES.KeySize = intDimensioneChiave 'dimensione del blocco objAES.BlockSize = 128 'carico il Vettore di Inizializzazione objAES.IV = bytIV 'carico la chiave trasformandola da string a array di byte objAES.Key = Encoding.Default.GetBytes(vstrChiave) 'cripto l'array di byte che contiene il file abytFileC = objAES.CreateEncryptor().TransformFinalBlock(abytFile, 0, abytFile.Length) Return abytFileC End Function Public Function Decripta(ByVal vstrChiave As String, ByVal vabytFileD As Byte()) As Byte() 'array di byte che contiene il file decriptato Dim abytFileD As Byte() 'inizializzo la classe RijndaelManaged Dim objAES As RijndaelManaged = New RijndaelManaged 'dimensione della chiave: 128 bit = 16 byte, 256 bit = 32 byte objAES.KeySize = intDimensioneChiave 'dimensione del blocco objAES.BlockSize = 128 'carico il Vettore di Inizializzazione objAES.IV = bytIV 'carico la chiave trasformandola da string a array di byte objAES.Key = Encoding.Default.GetBytes(vstrChiave) Try - 91 - 'decripto l'array di byte che contiene il file abytFileD = objAES.CreateDecryptor().TransformFinalBlock(abytFile, 0, abytFile.Length) Return abytFileD 'gestisco gli errori Catch ex As Exception abytFile = Nothing Return abytFile End Try End Function Public Function VerificaChiave(ByVal vstrChiave As String) As Boolean 'verifico la correttezza della chiave impostando che sia alfanumerica e di lunghezza giusta If vstrChiave = "" Then MsgBox("Non è stata inserita la chiave", MsgBoxStyle.Exclamation) Return False ElseIf Regex.IsMatch(vstrChiave, "[^a-zA-Z0-9]") Then 'se è sbagliata avverto l'utente MsgBox("La chiave non è valida: può contenere solo caratteri alfanumerici", MsgBoxStyle.Exclamation) Return False ElseIf vstrChiave.Length < intDimensioneChiave / 8 Then 'se è sbagliata avverto l'utente MsgBox("La chiave non è valida:" & _ " deve essere lunga: " & intDimensioneChiave / 8 & " caratteri", MsgBoxStyle.Exclamation) Return False Else Return True End If End Function Public Sub AggiungiEst() 'ridimensiono l'array contenente il file in modo da contenere i 3 caratteri dell'estensione in fondo Dim intLunghFile As Integer = abytFile.Length ReDim Preserve abytFile(intLunghFile + 2) 'prendo l'estensione del file e lo rendo di 3 caratteri togliendo il punto Dim strEstensioneFile As String = System.IO.Path.GetExtension(strPath) strEstensioneFile = Strings.Right(strEstensioneFile, 3) 'trasformo la stringa contenente l'estensione del file in un array di byte Dim abytEstensioneFile As Byte() abytEstensioneFile = Encoding.Default.GetBytes(strEstensioneFile) 'carico negli ultimi 3 posti dell'array contenente il file l'estensione del file Dim i As Integer For i = 0 To 2 abytFile(intLunghFile + i) = abytEstensioneFile(i) Next End Sub Public Sub RimuoviEst() 'ridimensiono l'array contenente il file in modo da eliminare i 3 caratteri dell'estensione in fondo Dim intLunghFile As Integer = abytFile.Length ReDim Preserve abytFile(intLunghFile - 4) End Sub - 92 - Private Sub cmdCripta_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles cmdCripta.Click 'metto il cursore a forma di clessidra Me.Cursor = Cursors.WaitCursor 'setto il Flag a 0 per dire che sto Criptando blnFlag = 0 txtChiavescelta.Text = "" Try 'leggo il file Call LeggiFile(strPath) 'verifico la correttezza della chiave If VerificaChiave(txtChiave.Text) = True Then 'aggiungo l'estensione al contenuto del file Call AggiungiEst() 'cripto il file abytFile = Cripta(bytIV, txtChiave.Text, abytFile) 'scrivo il file Call ScriviFile(strPath, abytFile) txtChiavescelta.Text = txtChiave.Text 'inizializzo Call init() Else txtChiave.Text = "" txtChiave.Select() End If Catch ex As Exception If strPath = "" Then MsgBox("Non è stato scelto nessun file", MsgBoxStyle.Exclamation) Else MsgBox("Non è possibile aprire il file", MsgBoxStyle.Exclamation) strPath = "" End If txtPath.Text = "" txtPath.Select() End Try 'metto il cursore di default Me.Cursor = Cursors.Default End Sub Private Sub cmdDecripta_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles cmdDecripta.Click 'metto il cursore a forma di clessidra Me.Cursor = Cursors.WaitCursor 'setto il Flag ad 1 per dire che sto Decriptando blnFlag = 1 txtChiavescelta.Text = "" Try 'leggo il file Call LeggiFile(strPath) If VerificaChiave(txtChiave.Text) = True Then 'decripto il file abytFile = Decripta(txtChiave.Text, abytFile) 'gestisco l'errore dovuto alla funzione decripta se la chiave è errata If abytFile IsNot Nothing Then 'leggo l'estensione dall'array contenente il file decriptato 'che serve per inserirla nella funzione per scriverlo Dim intLunghFile As Integer = abytFile.Length - 1 Dim abytEstensioneFile(2) As Byte Dim i As Integer - 93 - For i = 0 To 2 abytEstensioneFile(2 - i) = abytFile(intLunghFile - i) Next strEstensioneFile = Encoding.Default.GetString(abytEstensioneFile) 'rimuovo l'estensione dal contenuto del file Call RimuoviEst() 'scrivo il file Call ScriviFile(strPath, abytFile) 'inizializzo Call init() Else MsgBox("Non è possibile decriptare il file", MsgBoxStyle.Exclamation) 'inizializzo Call init() End If Else txtChiave.Text = "" txtChiave.Select() End If Catch ex As Exception If strPath = "" Then MsgBox("Non è stato scelto nessun file", MsgBoxStyle.Exclamation) Else MsgBox("Non è possibile aprire il file", MsgBoxStyle.Exclamation) strPath = "" End If txtPath.Text = "" txtPath.Select() End Try 'metto il cursore di default Me.Cursor = Cursors.Default End Sub Private Sub init() 'inizializzo le variabili pubbliche abytFile = Nothing strPath = "" txtPath.Text = "" txtChiave.Text = "" blnFlag = 0 End Sub Private Sub frmMain_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load ‘inizializzo Call init() 'inizializzo la dimensione della chiave a 256 bit rbt256bit.Checked = True intDimensioneChiave = 256 txtChiave.MaxLength = 32 rbt128bit.Checked = False txtPath.Select() End Sub - 94 - Private Sub rbt128bit_CheckedChanged(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles rbt128bit.CheckedChanged 'gestisco le opzioni per la lunghezza della chiave If rbt128bit.Checked = True Then intDimensioneChiave = 128 txtChiave.MaxLength = 16 rbt256bit.Checked = False End If End Sub Private Sub rbt256bit_CheckedChanged(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles rbt256bit.CheckedChanged 'gestisco le opzioni per la lunghezza della chiave If rbt256bit.Checked = True Then intDimensioneChiave = 256 txtChiave.MaxLength = 32 rbt128bit.Checked = False End If End Sub End Class - 95 - Codice VB.NET del software di steganografia Option Explicit On Imports System.IO Imports System.Text Imports System.Text.RegularExpressions Public Class frmMain Public strPath As String 'contiene il Path del file Public abytFile As Byte() 'contiene il file in byte Public intDimensioneChiave As Integer 'contiene la dimensione della chiave #Region "Drag&Drop" Private Sub txtPath_DragDrop(ByVal sender As Object, _ ByVal e As System.Windows.Forms.DragEventArgs) Handles txtPath.DragDrop If (e.Data.GetDataPresent(DataFormats.FileDrop)) Then e.Effect = DragDropEffects.Copy Else e.Effect = DragDropEffects.None End If End Su Private Sub txtPath_DragEnter(ByVal sender As Object, _ ByVal e As System.Windows.Forms.DragEventArgs) Handles txtPath.DragEnter Dim sPathName As String() sPathName = e.Data.GetData(DataFormats.FileDrop) txtPath.Text = sPathName(0) strPath = sPathName(0) lblBitByteModificati.Text = "Utilizzo bit: " txtRecuperochiave.Text = "" End Sub Private Sub txtPath_DragLeave(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles txtPath.DragLeave txtPath.Text = strPath 'se il file non è una Bitmap inizializzo If VerificaFile(strPath) = True Then Call Init() End If End Sub Public Function VerificaFile(ByVal vstrPathName As String) As Boolean 'verifico che l'estensione del file sia BMP If System.IO.Path.GetExtension(vstrPathName) <> ".bmp" Then MsgBox("Il file selezionato non è un'immagine Bitmap", MsgBoxStyle.Exclamation) Return True End If End Function #End Region - 96 - #Region "Funzioni per Lettura e Scrittura" Public Function LeggiFile(ByVal vstrPathName As String) As Byte() 'leggo il file usando BinaryReader in modo da ottenere un'array di byte Dim objFileL As New FileStream(vstrPathName, FileMode.OpenOrCreate, FileAccess.Read) Dim objFileLBR As New BinaryReader(objFileL) abytFile = objFileLBR.ReadBytes(objFileL.Length) objFileL.Close() objFileLBR.Close() Return abytFile End Function Private Sub ScriviFile(ByVal vstrPathName As String, ByVal vabytFileS As Byte()) 'sfruttando la classe Path ottengo: directory e nome del file Dim strDirFile As String = Path.GetDirectoryName(vstrPathName) Dim strNomeFile As String = Path.GetFileNameWithoutExtension(vstrPathName) 'se il file esiste già aggiungo al nome un trattino If File.Exists(strDirFile & "\" & strNomeFile & ".bmp") Then strNomeFile &= "_" Else strNomeFile &= "" End If Dim objFileS As New FileStream(strDirFile & "\" & strNomeFile & _ ".bmp", FileMode.Create, FileAccess.Write) Dim objFileSBW As New BinaryWriter(objFileS) objFileSBW.Write(vabytFileS) objFileS.Close() objFileSBW.Close() End Sub #End Region Public Sub Init() txtChiave.Text = "" lblBitByteModificati.Text = "Utilizzo bit: " txtPath.Text = "" strPath = "" txtRecuperochiave.Text = "" End Sub Private Sub cmdStego_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles cmdStego.Click 'verifico la correttezza della chiave If Regex.IsMatch(txtChiave.Text, "[^0-9a-zA-Z]") Then 'se è sbagliata avverto l'utente MsgBox("La chiave non è valida: può contenere solo caratteri alfanumerici", MsgBoxStyle.Exclamation) txtChiave.Text = "" txtChiave.Select() Exit Sub End If 'metto il cursore a forma di clessidra Me.Cursor = Cursors.WaitCursor Try Dim a As Integer Dim b As Integer Dim aintCar(62) As Integer - 97 - Dim aintCarShift(62) As Integer 'popolo aintCaratteri con le posizione dei pixel neri For a = 0 To 62 aintCar(a) = a Next 'shifto la aintCaratteri in base allo Shift richiesto dall'utente come usando un cifrario a scorrimento For a = 0 To 62 If (a + txtCarShift.Text) <= 62 Then aintCarShift(a + txtCarShift.Text) = aintCar(a) End If Next For b = 0 To txtCarShift.Text - 1 aintCarShift(b) = aintCar(63 - txtCarShift.Text + b) Next 'prendo la chiave dell'utente e vado ad inserire su aintNumPixCar il numero 'equivalente alla posizione dei caratteri della chiave 'contiene il pixel nero dei caratteri della chiave Dim aintNumPixCar(intDimensioneChiave - 1) As Integer Dim aintPosPixCar(intDimensioneChiave - 1) As Integer For a = 0 To intDimensioneChiave - 1 Select Case txtChiave.Text.Substring(a, 1) 'scorro un carattere alla volta la chiave Case Is = "0" aintNumPixCar(a) = 0 Case Is = "1" aintNumPixCar(a) = 1 Case Is = "2" aintNumPixCar(a) = 2 Case Is = "3" aintNumPixCar(a) = 3 Case Is = "4" aintNumPixCar(a) = 4 Case Is = "5" aintNumPixCar(a) = 5 Case Is = "6" aintNumPixCar(a) = 6 Case Is = "7" aintNumPixCar(a) = 7 Case Is = "8" aintNumPixCar(a) = 8 Case Is = "9" aintNumPixCar(a) = 9 Case Is = "a" aintNumPixCar(a) = 10 Case Is = "b" aintNumPixCar(a) = 11 Case Is = "c" aintNumPixCar(a) = 12 Case Is = "d" aintNumPixCar(a) = 13 Case Is = "e" aintNumPixCar(a) = 14 Case Is = "f" aintNumPixCar(a) = 15 Case Is = "g" aintNumPixCar(a) = 16 Case Is = "h" aintNumPixCar(a) = 17 Case Is = "i" aintNumPixCar(a) = 18 Case Is = "j" - 98 - aintNumPixCar(a) = 19 Case Is = "k" aintNumPixCar(a) = 20 Case Is = "l" aintNumPixCar(a) = 21 Case Is = "m" aintNumPixCar(a) = 22 Case Is = "n" aintNumPixCar(a) = 23 Case Is = "o" aintNumPixCar(a) = 24 Case Is = "p" aintNumPixCar(a) = 25 Case Is = "q" aintNumPixCar(a) = 26 Case Is = "r" aintNumPixCar(a) = 27 Case Is = "s" aintNumPixCar(a) = 28 Case Is = "t" aintNumPixCar(a) = 29 Case Is = "u" aintNumPixCar(a) = 30 Case Is = "v" aintNumPixCar(a) = 31 Case Is = "w" aintNumPixCar(a) = 32 Case Is = "x" aintNumPixCar(a) = 33 Case Is = "y" aintNumPixCar(a) = 34 Case Is = "z" aintNumPixCar(a) = 35 Case Is = "A" aintNumPixCar(a) = 36 Case Is = "B" aintNumPixCar(a) = 37 Case Is = "C" aintNumPixCar(a) = 38 Case Is = "D" aintNumPixCar(a) = 39 Case Is = "E" aintNumPixCar(a) = 40 Case Is = "F" aintNumPixCar(a) = 41 Case Is = "G" aintNumPixCar(a) = 42 Case Is = "H" aintNumPixCar(a) = 43 Case Is = "I" aintNumPixCar(a) = 44 Case Is = "J" aintNumPixCar(a) = 45 Case Is = "K" aintNumPixCar(a) = 46 Case Is = "L" aintNumPixCar(a) = 47 Case Is = "M" aintNumPixCar(a) = 48 Case Is = "N" - 99 - aintNumPixCar(a) = 49 Case Is = "O" aintNumPixCar(a) = 50 Case Is = "P" aintNumPixCar(a) = 51 Case Is = "Q" aintNumPixCar(a) = 52 Case Is = "R" aintNumPixCar(a) = 53 Case Is = "S" aintNumPixCar(a) = 54 Case Is = "T" aintNumPixCar(a) = 55 Case Is = "U" aintNumPixCar(a) = 56 Case Is = "V" aintNumPixCar(a) = 57 Case Is = "W" aintNumPixCar(a) = 58 Case Is = "X" aintNumPixCar(a) = 59 Case Is = "Y" aintNumPixCar(a) = 60 Case Is = "Z" aintNumPixCar(a) = 61 Case Is = "_" aintNumPixCar(a) = 62 End Select aintPosPixCar(a) = aintCarShift(aintNumPixCar(a)) Next ‘leggo il file Call LeggiFile(strPath) 'valuto se il file è troppo piccolo per contenere la chiave If abytFile.Length > (intDimensioneChiave * 64 + 54) Then 'contiene il numero di bit da utilizzare per la modifica Dim intNumBit As Integer = txtNumBit.Text - 1 Dim abytCella(63) As Byte 'contiene la matrice di un carattere Dim intPix As Integer 'contiene il byte da elaborare 'contatore per il ciclo del numero di caratteri della chiave Dim intContDimChiave As Integer 'contatore per il ciclo del numero di pixel della matrice di codifica Dim intContDimMatPix As Integer 'contatore per il ciclo del numero di bit da utilizzare Dim intContNumBit As Integer Dim intBitModificati As Integer = 0 'contiene il numero di bit modificati Dim intByteModificati As Integer = 0 'contiene il numero di byte modificati For intContDimChiave = 0 To intDimensioneChiave - 1 'vado a modificare la matrice in base alla chiave For intContDimMatPix = 0 To 63 'carico la matrice di 64 pixel abytCella(intContDimMatPix) = abytFile(54 + intContDimMatPix + _ (intContDimChiave * 64)) Dim resto As Integer 'verifico la condizione sul resto resto = 0 intPix = abytCella(intContDimMatPix) For intContNumBit = 0 To intNumBit If CBool(intPix And (1 << intNumBit - intContNumBit)) = True Then resto += 2 ^ (intNumBit - intContNumBit) End If - 100 - Next If intContDimMatPix = aintPosPixCar(intContDimChiave) Then 'pixel nero 'se resto=0 allora è già corretto If resto > 0 Then abytFile(54 + intContDimMatPix + (intContDimChiave * 64)) -= resto 'conto i byte modificati intByteModificati += 1 'conto i bit cambiati Dim intContBitModificati As Integer Dim aintConfronto() As Integer = {128, 64, 32, 16, 8, 4, 2, 1} For intContBitModificati = 0 To 7 If (resto And aintConfronto(intContBitModificati)) _ = aintConfronto(intContBitModificati) Then intBitModificati += 1 End If Next End If Else 'pixel bianco 'se resto>1 allora è già corretto If resto = 0 Then abytFile(54 + intContDimMatPix + (intContDimChiave * 64)) += 1 'conto i byte modificati intByteModificati += 1 'conto i bit modificati intBitModificati += 1 End If End If Next Next 'scrivo il file Call ScriviFile(strPath, abytFile) 'scrivo nella txtBox il numero di bit modificati in relazione ai byte modificati Dim strMessaggio As String = Nothing Select Case intBitModificati / intByteModificati Case Is = 0 strMessaggio = " media 0 bit per byte" Case Is = 1 strMessaggio = " media 1 bit per byte" Case Is > 1 strMessaggio = " media " & _ Math.Round(intBitModificati / intByteModificati, 2) & " bit per byte" End Select lblBitByteModificati.Text = "Utilizzo bit: variati " & intBitModificati & _ " bit su " & intByteModificati & " byte:" & strMessaggio Else MsgBox("Il file .bmp non è abbastanza grande", MsgBoxStyle.Exclamation) End If txtChiave.Text = "" txtPath.Text = "" strPath = "" txtPath.Select() Catch ex As Exception 'verifico che Path<>"" If strPath = "" Then MsgBox("Non è stato scelto nessun file", MsgBoxStyle.Exclamation) txtPath.Select() 'End If 'verifico la correttezza della chiave - 101 - ElseIf txtChiave.Text = "" Then MsgBox("Non è stata inserita la chiave", MsgBoxStyle.Exclamation) txtChiave.Select() ElseIf txtChiave.Text.Length < intDimensioneChiave Then 'se è sbagliata avverto l'utente MsgBox("La chiave non è valida:" & _ " deve essere lunga: " & intDimensioneChiave & " caratteri", MsgBoxStyle.Exclamation) txtChiave.Text = "" txtChiave.Select() End If End Try 'metto il cursore di default Me.Cursor = Cursors.Default End Sub Private Sub cmdDeStego_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles cmdDeStego.Click txtRecuperochiave.Text = "" 'metto il cursore a forma di clessidra Me.Cursor = Cursors.WaitCursor Try Call LeggiFile(strPath) 'contiene l'offset dei caratteri nella matrice 8x8 Dim aintPosPixCar(intDimensioneChiave - 1) As Integer 'contiene il numero di bit da utilizzare per la modifica Dim intNumBit As Integer = txtNumBit.Text - 1 Dim abytCella(63) As Byte 'contiene la matrice di un carattere 'contatore per il ciclo del numero di caratteri della chiave Dim intContDimChiave As Integer 'contatore per il ciclo del numero di pixel della matrice di codifica Dim intContDimMatPix As Integer 'contatore per il ciclo del numero di bit da utilizzare Dim intContNumBit As Integer For intContDimChiave = 0 To intDimensioneChiave - 1 'vado a modificare la matrice in base alla chiave For intContDimMatPix = 0 To 63 'carico la matrice di 64 pixel abytCella(intContDimMatPix) = abytFile(54 + intContDimMatPix + (intContDimChiave * 64)) Dim resto As Integer 'verifico la condizione sul resto resto = 0 For intContNumBit = 0 To intNumBit If CBool(abytCella(intContDimMatPix) And (1 << intNumBit - intContNumBit)) = True Then resto += 2 ^ (intNumBit - intContNumBit) End If Next 'pixel nero If resto = 0 Then aintPosPixCar(intContDimChiave) = intContDimMatPix End If Next Next Dim astrConfronto() As String = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9" _ , "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p" _ , "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" _ , "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P" _ , "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "_"} Dim a As Integer - 102 - 'prendo i caratteri shiftati corrisponendi alla chiave e li metto nella txtChiave For a = 0 To intDimensioneChiave - 1 If (aintPosPixCar(a) + txtCarShift.Text) <= 62 Then txtRecuperochiave.Text &= astrConfronto(aintPosPixCar(a) + txtCarShift.Text) Else txtRecuperochiave.Text &= astrConfronto(aintPosPixCar(a) + txtCarShift.Text - 63) End If Next Catch ex As Exception If strPath = "" Then MsgBox("Non è stato scelto nessun file", MsgBoxStyle.Exclamation) End If End Try 'metto il cursore di default Me.Cursor = Cursors.Default strPath = "" txtPath.Text = "" txtPath.Select() End Sub Private Sub frmMain_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load 'inizializzo la dimensione della chiave a 256 bit rbt256bit.Checked = True intDimensioneChiave = 32 txtChiave.MaxLength = 32 rbt128bit.Checked = False 'inizializza il numero dei bit e lo shift txtNumBit.Text = 3 txtCarShift.Text = 50 End Sub Private Sub rbt128bit_CheckedChanged(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles rbt128bit.CheckedChanged 'gestisco le opzioni per la lunghezza della chiave If rbt128bit.Checked = True Then intDimensioneChiave = 16 txtChiave.MaxLength = 16 rbt256bit.Checked = False End If End Sub Private Sub rbt256bit_CheckedChanged(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles rbt256bit.CheckedChanged 'gestisco le opzioni per la lunghezza della chiave If rbt256bit.Checked = True Then intDimensioneChiave = 32 txtChiave.MaxLength = 32 rbt128bit.Checked = False End If End Sub End Class - 103 - Codice ASP del software web Script per la funzione di hash MD5 - Autore: Phil Fresle <% Private Const BITS_TO_A_BYTE=8 Private Const BYTES_TO_A_WORD=4 Private Const BITS_TO_A_WORD=32 Private m_lOnBits(30) Private m_l2Power(30) m_lOnBits(0)=CLng(1) m_lOnBits(1)=CLng(3) m_lOnBits(2)=CLng(7) m_lOnBits(3)=CLng(15) m_lOnBits(4)=CLng(31) m_lOnBits(5)=CLng(63) m_lOnBits(6)=CLng(127) m_lOnBits(7)=CLng(255) m_lOnBits(8)=CLng(511) m_lOnBits(9)=CLng(1023) m_lOnBits(10)=CLng(2047) m_lOnBits(11)=CLng(4095) m_lOnBits(12)=CLng(8191) m_lOnBits(13)=CLng(16383) m_lOnBits(14)=CLng(32767) m_lOnBits(15)=CLng(65535) m_lOnBits(16)=CLng(131071) m_lOnBits(17)=CLng(262143) m_lOnBits(18)=CLng(524287) m_lOnBits(19)=CLng(1048575) m_lOnBits(20)=CLng(2097151) m_lOnBits(21)=CLng(4194303) m_lOnBits(22)=CLng(8388607) m_lOnBits(23)=CLng(16777215) m_lOnBits(24)=CLng(33554431) m_lOnBits(25)=CLng(67108863) m_lOnBits(26)=CLng(134217727) m_lOnBits(27)=CLng(268435455) m_lOnBits(28)=CLng(536870911) m_lOnBits(29)=CLng(1073741823) m_lOnBits(30)=CLng(2147483647) m_l2Power(0)=CLng(1) m_l2Power(1)=CLng(2) m_l2Power(2)=CLng(4) m_l2Power(3)=CLng(8) m_l2Power(4)=CLng(16) m_l2Power(5)=CLng(32) m_l2Power(6)=CLng(64) m_l2Power(7)=CLng(128) m_l2Power(8)=CLng(256) m_l2Power(9)=CLng(512) m_l2Power(10)=CLng(1024) m_l2Power(11)=CLng(2048) m_l2Power(12)=CLng(4096) m_l2Power(13)=CLng(8192) m_l2Power(14)=CLng(16384) m_l2Power(15)=CLng(32768) m_l2Power(16)=CLng(65536) m_l2Power(17)=CLng(131072) - 104 - m_l2Power(18)=CLng(262144) m_l2Power(19)=CLng(524288) m_l2Power(20)=CLng(1048576) m_l2Power(21)=CLng(2097152) m_l2Power(22)=CLng(4194304) m_l2Power(23)=CLng(8388608) m_l2Power(24)=CLng(16777216) m_l2Power(25)=CLng(33554432) m_l2Power(26)=CLng(67108864) m_l2Power(27)=CLng(134217728) m_l2Power(28)=CLng(268435456) m_l2Power(29)=CLng(536870912) m_l2Power(30)=CLng(1073741824) Private Function LShift(lValue,iShiftBits) If iShiftBits=0 Then LShift=lValue Exit Function ElseIf iShiftBits=31 Then If lValue And 1 Then LShift=&H80000000 Else LShift=0 End If Exit Function ElseIf iShiftBits<0 Or iShiftBits>31 Then Err.Raise 6 End If If (lValue And m_l2Power(31-iShiftBits)) Then LShift=((lValue And m_lOnBits(31-(iShiftBits+1)))*m_l2Power(iShiftBits)) Or &H80000000 Else LShift=((lValue And m_lOnBits(31-iShiftBits))*m_l2Power(iShiftBits)) End If End Function Private Function RShift(lValue,iShiftBits) If iShiftBits=0 Then RShift=lValue Exit Function ElseIf iShiftBits=31 Then If lValue And &H80000000 Then RShift=1 Else RShift=0 End If Exit Function ElseIf iShiftBits<0 Or iShiftBits>31 Then Err.Raise 6 End If RShift=(lValue And &H7FFFFFFE)\m_l2Power(iShiftBits) If (lValue And &H80000000) Then RShift=(RShift Or (&H40000000\m_l2Power(iShiftBits-1))) End If End Function Private Function RotateLeft(lValue,iShiftBits) RotateLeft=LShift(lValue,iShiftBits) Or RShift(lValue,(32-iShiftBits)) End Function - 105 - Private Function AddUnsigned(lX,lY) Dim lX4 Dim lY4 Dim lX8 Dim lY8 Dim lResult lX8=lX And &H80000000 lY8=lY And &H80000000 lX4=lX And &H40000000 lY4=lY And &H40000000 lResult=(lX And &H3FFFFFFF)+(lY And &H3FFFFFFF) If lX4 And lY4 Then lResult=lResult Xor &H80000000 Xor lX8 Xor lY8 ElseIf lX4 Or lY4 Then If lResult And &H40000000 Then lResult=lResult Xor &HC0000000 Xor lX8 Xor lY8 Else lResult=lResult Xor &H40000000 Xor lX8 Xor lY8 End If Else lResult=lResult Xor lX8 Xor lY8 End If AddUnsigned=lResult End Function Private Function F(x,y,z) F=(x And y) Or ((Not x) And z) End Function Private Function G(x,y,z) G=(x And z) Or (y And (Not z)) End Function Private Function H(x,y,z) H=(x Xor y Xor z) End Function Private Function I(x,y,z) I=(y Xor (x Or (Not z))) End Function Private Sub FF(a,b,c,d,x,s,ac) a=AddUnsigned(a,AddUnsigned(AddUnsigned(F(b,c,d),x),ac)) a=RotateLeft(a,s) a=AddUnsigned(a,b) End Sub Private Sub GG(a,b,c,d,x,s,ac) a=AddUnsigned(a,AddUnsigned(AddUnsigned(G(b,c,d),x),ac)) a=RotateLeft(a,s) a=AddUnsigned(a,b) End Sub Private Sub HH(a,b,c,d,x,s,ac) a=AddUnsigned(a,AddUnsigned(AddUnsigned(H(b,c,d),x),ac)) a=RotateLeft(a,s) a=AddUnsigned(a,b) End Sub - 106 - Private Sub II(a,b,c,d,x,s,ac) a=AddUnsigned(a,AddUnsigned(AddUnsigned(I(b,c,d),x),ac)) a=RotateLeft(a,s) a=AddUnsigned(a,b) End Sub Private Function ConvertToWordArray(sMessage) Dim lMessageLength Dim lNumberOfWords Dim lWordArray() Dim lBytePosition Dim lByteCount Dim lWordCount Dim lByteValue ' need these variables to handle byte value and input argument type Dim lMessageType Const MODULUS_BITS=512 Const CONGRUENT_BITS=448 lMessageType=Vartype(sMessage) Select Case lMessageType ' strings or Variant Byte Arrays: nothing else! Case 8 : lMessageLength=Len(sMessage) Case 8209 : lMessageLength=LenB(sMessage) Case Else Err.Raise -1,"MD5","Unknown Type passed to MD5 function" End Select lNumberOfWords=(((lMessageLength+((MODULUS_BITSCONGRUENT_BITS)\BITS_TO_A_BYTE))\(MODULUS_BITS\BITS_TO_A_BYTE))+1)* _ (MODULUS_BITS\BITS_TO_A_WORD) ReDim lWordArray(lNumberOfWords-1) lBytePosition=0 lByteCount=0 Do Until lByteCount >=lMessageLength lWordCount=lByteCount\BYTES_TO_A_WORD lBytePosition=(lByteCount Mod BYTES_TO_A_WORD)*BITS_TO_A_BYTE Select Case lMessageType ' get the next byte value Case 8 : lByteValue = Asc (Mid (sMessage,lByteCount+1,1)) Case 8209 : lByteValue = AscB(MidB(sMessage,lByteCount+1,1)) End Select lWordArray(lWordCount)=lWordArray(lWordCount) Or LShift(lByteValue,lBytePosition) lByteCount=lByteCount+1 Loop lWordCount=lByteCount\BYTES_TO_A_WORD lBytePosition=(lByteCount Mod BYTES_TO_A_WORD)*BITS_TO_A_BYTE lWordArray(lWordCount)=lWordArray(lWordCount) Or LShift(&H80,lBytePosition) lWordArray(lNumberOfWords-2)=LShift(lMessageLength,3) lWordArray(lNumberOfWords-1)=RShift(lMessageLength,29) ConvertToWordArray=lWordArray End Function Private Function WordToHex(lValue) Dim lByte Dim lCount For lCount=0 To 3 lByte=RShift(lValue,lCount*BITS_TO_A_BYTE) And m_lOnBits(BITS_TO_A_BYTE-1) WordToHex=WordToHex & Right("0" & Hex(lByte),2) Next End Function - 107 - Public Function MD5(sMessage) Dim x Dim k Dim AA Dim BB Dim CC Dim DD Dim a Dim b Dim c Dim d Const S11=7 Const S12=12 Const S13=17 Const S14=22 Const S21=5 Const S22=9 Const S23=14 Const S24=20 Const S31=4 Const S32=11 Const S33=16 Const S34=23 Const S41=6 Const S42=10 Const S43=15 Const S44=21 x=ConvertToWordArray(sMessage) a=&H67452301 b=&HEFCDAB89 c=&H98BADCFE d=&H10325476 For k=0 To UBound(x) Step 16 AA=a BB=b CC=c DD=d FF a,b,c,d,x(k+0),S11,&HD76AA478 FF d,a,b,c,x(k+1),S12,&HE8C7B756 FF c,d,a,b,x(k+2),S13,&H242070DB FF b,c,d,a,x(k+3),S14,&HC1BDCEEE FF a,b,c,d,x(k+4),S11,&HF57C0FAF FF d,a,b,c,x(k+5),S12,&H4787C62A FF c,d,a,b,x(k+6),S13,&HA8304613 FF b,c,d,a,x(k+7),S14,&HFD469501 FF a,b,c,d,x(k+8),S11,&H698098D8 FF d,a,b,c,x(k+9),S12,&H8B44F7AF FF c,d,a,b,x(k+10),S13,&HFFFF5BB1 FF b,c,d,a,x(k+11),S14,&H895CD7BE FF a,b,c,d,x(k+12),S11,&H6B901122 FF d,a,b,c,x(k+13),S12,&HFD987193 FF c,d,a,b,x(k+14),S13,&HA679438E FF b,c,d,a,x(k+15),S14,&H49B40821 GG a,b,c,d,x(k+1),S21,&HF61E2562 GG d,a,b,c,x(k+6),S22,&HC040B340 GG c,d,a,b,x(k+11),S23,&H265E5A51 GG b,c,d,a,x(k+0),S24,&HE9B6C7AA GG a,b,c,d,x(k+5),S21,&HD62F105D GG d,a,b,c,x(k+10),S22,&H2441453 GG c,d,a,b,x(k+15),S23,&HD8A1E681 - 108 - GG b,c,d,a,x(k+4),S24,&HE7D3FBC8 GG a,b,c,d,x(k+9),S21,&H21E1CDE6 GG d,a,b,c,x(k+14),S22,&HC33707D6 GG c,d,a,b,x(k+3),S23,&HF4D50D87 GG b,c,d,a,x(k+8),S24,&H455A14ED GG a,b,c,d,x(k+13),S21,&HA9E3E905 GG d,a,b,c,x(k+2),S22,&HFCEFA3F8 GG c,d,a,b,x(k+7),S23,&H676F02D9 GG b,c,d,a,x(k+12),S24,&H8D2A4C8A HH a,b,c,d,x(k+5),S31,&HFFFA3942 HH d,a,b,c,x(k+8),S32,&H8771F681 HH c,d,a,b,x(k+11),S33,&H6D9D6122 HH b,c,d,a,x(k+14),S34,&HFDE5380C HH a,b,c,d,x(k+1),S31,&HA4BEEA44 HH d,a,b,c,x(k+4),S32,&H4BDECFA9 HH c,d,a,b,x(k+7),S33,&HF6BB4B60 HH b,c,d,a,x(k+10),S34,&HBEBFBC70 HH a,b,c,d,x(k+13),S31,&H289B7EC6 HH d,a,b,c,x(k+0),S32,&HEAA127FA HH c,d,a,b,x(k+3),S33,&HD4EF3085 HH b,c,d,a,x(k+6),S34,&H4881D05 HH a,b,c,d,x(k+9),S31,&HD9D4D039 HH d,a,b,c,x(k+12),S32,&HE6DB99E5 HH c,d,a,b,x(k+15),S33,&H1FA27CF8 HH b,c,d,a,x(k+2),S34,&HC4AC5665 II a,b,c,d,x(k+0),S41,&HF4292244 II d,a,b,c,x(k+7),S42,&H432AFF97 II c,d,a,b,x(k+14),S43,&HAB9423A7 II b,c,d,a,x(k+5),S44,&HFC93A039 II a,b,c,d,x(k+12),S41,&H655B59C3 II d,a,b,c,x(k+3),S42,&H8F0CCC92 II c,d,a,b,x(k+10),S43,&HFFEFF47D II b,c,d,a,x(k+1),S44,&H85845DD1 II a,b,c,d,x(k+8),S41,&H6FA87E4F II d,a,b,c,x(k+15),S42,&HFE2CE6E0 II c,d,a,b,x(k+6),S43,&HA3014314 II b,c,d,a,x(k+13),S44,&H4E0811A1 II a,b,c,d,x(k+4),S41,&HF7537E82 II d,a,b,c,x(k+11),S42,&HBD3AF235 II c,d,a,b,x(k+2),S43,&H2AD7D2BB II b,c,d,a,x(k+9),S44,&HEB86D391 a=AddUnsigned(a,AA) b=AddUnsigned(b,BB) c=AddUnsigned(c,CC) d=AddUnsigned(d,DD) Next MD5=LCase(WordToHex(a) & WordToHex(b) & WordToHex(c) & WordToHex(d)) End Function %> - 109 - Script per il login <% 'funzione per prevenire attacchi di SQL injection 'al posto dei caratteri sensibili inserisco caratteri innoqui Function FiltraSQL(strSQL) strSQL = Replace(strSQL, "'", "''") strSQL = Replace(strSQL, "%", "[%]") strSQL = Replace(strSQL, "[", "[[]") strSQL = Replace(strSQL, "]", "[]]") strSQL = Replace(strSQL, "_", "[_]") strSQL = Replace(strSQL, "#", "[#]") FiltraSQL = strSQL End function 'se è stato premuto il bottone Login If Request.Form("Login")="Login" then 'recupero l'Username Username = Request.Form("username") 'recupero la Password e ne faccio l'hash Password = MD5(Request.Form("password")) 'verifico che i campi "Username" e "Password" non siano vuoti If Username <> "" and Password <> "" then 'i dati sono corretti, posso iniziare il controllo sul database 'mi connetto al database url_DB = "driver={Microsoft Access Driver (*.mdb)};dbq=" & server.mappath("db/user.mdb") Set Conn = Server.CreateObject("ADODB.Connection") conn.Open url_DB Set RecSet = Server.CreateObject("ADODB.Recordset") 'preparo la query filtrata contro SQL injection SQL = "SELECT * FROM Utenti where Username = '" & FiltraSQL(Username) &_ "' and Password = '" & FiltraSQL(Password) & "' " 'RecSet.Open SQL, Conn, adOpenStatic, adLockOptimistic RecSet.Open SQL, Conn, adOpenStatic 'verifico se l'utente esiste If Not RecSet.Eof then 'l'utente esiste allora imposto 2 sessioni: Log=True e Username=Username e indirizzo all’area riservata Session("Log") = True Session("Username") = Username Response.Redirect "h_arearis.asp" Else 'l'utente non esiste allora imposto Log=False Session("Log") = False End If ‘Chiudo la connessione al DB RecSet.Close Set RecSet = Nothing Conn.Close Set Conn = Nothing ‘verifico se l'utente è loggato If Session("Log") = False then ‘i dati non sono correti: utente inesistente o username/password errati msg="<p><h2><b>Accesso negato!</b></h2><p>L'UserName o la Password sono errati" End If Else ' campi "username" o "password" vuoti msg="<p><h2><b>Accesso negato!</b></h2><p>I Campi UserName o Password sono vuoti" End If End If %> - 110 - <!--form per il login--> <center> <%=msg%> <form method="POST" action=""> <table border="0" cellspacing="0" cellpadding="0"> <tr><td width=75>UserName:</td><td><INPUT TYPE=Text NAME="username" size="12"></td></tr> <tr><td width=75>Password:</td><td><INPUT TYPE=Password NAME="password" size="12"></td></tr> </table> <input type="submit" value="Login" name="Login"> </form> <script>document.login.username.focus();</script> </center> <!--form per il login--> - 111 - Script da inserire su ogni pagina dell'area riservata <% 'setto la pagina in modo che non sia accessibile dopo il logout tramite il tasto indietro del browser 'faccio in modo tale che il browser non memorizzi le pagine dell'area riservata nella cache Response.Buffer = True Response.ExpiresAbsolute = Now()-1 Response.Expires = 0 Response.AddHeader "pragma", "no-cache" Response.AddHeader "cache-control", "no-store, must-revalidate, private" Response.CacheControl = "private" 'la pagina non essendo nella cache quando viene premuto il tasto indietro del browser deve essere ricaricata 'se la lunghezza in caratteri della variabile Session("PrimaVisita") >0 ‘allora l'utente non è la prima volta che visita la pagina If len(Session("PrimaVisita"))>0 then Session("PrimaVisita") = "" Response.Redirect("h_login.asp") Response.End End If %> <% 'verifico se l'utente che vuole accedere è stato loggato, se non lo è lo reindirizzo alla pagina del login If Session("Log") = False and Session("Username") = "" then Response.Redirect "h_login.asp" End If %> - 112 - Classe per la gestione degli upload - Autore: Simone Medas <SCRIPT LANGUAGE=vbscript RUNAT=Server> Dim Inputs 'class to store data input class clsInput public name public isFile private pContent public size public fileName public filePath public property let content(newContent) pContent = newContent end property public default property get content() content = pContent end property 'save the content into file with same filename as uploaded in current application path public function saveFile() saveFile = saveFileAs("", "") end function 'save the content into file with given filename and path public function saveFileAs(byVal destinationPath, byVal newFileName) Dim withName 'if content is empty don't save it if size = 0 then exit function 'append given path if any if destinationPath <> "" then withName = destinationPath else withName = Server.MapPath(".") end if 'check if final slash exist, if don't, then do append it if right(withName, 1) <> "\" then withName = withName & "\" 'append new filename if any if newFileName <> "" then withName = withName & newFileName else withName = withName & fileName end if on error resume next Set fso = CreateObject("Scripting.FileSystemObject") Set tf = fso.CreateTextFile(withName , True) if Err.number = 0 then tf.Write(pContent) tf.Close end if if Err.number >0 then saveFileAs = Err.Description else saveFileAs = "" end if end function end class - 113 - Function UpLoad() Dim bytArray Dim bytecount bytecount = Request.TotalBytes if bytecount = 0 then UpLoad = 0 exit function end if 'continue only when bytecount>0 Set inputs = Server.CreateObject("Scripting.Dictionary") bytArray = Request.BinaryRead(bytecount) Dim objInput Dim adLongVarChar adLongVarChar = 201 Dim strByteToString Dim lngLoop Dim strIDInput Dim lngStartPos Dim lngFieldStartPos Dim strInputHeader Dim strContetType Dim strInputName Dim strFileName Dim strFilePath Dim strInputContent Dim isFile Dim rstTemp 'we using anrecordset object to quickly convert array of byte to string Set rstTemp = Server.CreateObject("ADODB.Recordset") rstTemp.Fields.Append "bytArray", adLongVarChar, lenb(bytArray) rstTemp.Open rstTemp.AddNew rstTemp.Fields("bytArray").AppendChunk bytArray rstTemp.Update 'we have getted the original string strByteToString = rstTemp("bytArray") Set rstTemp=Nothing lngStartPos = 1 'get row separation strIDInput = Mid(strByteToString, 1, InStr(1, strByteToString, vbCrLf) - 1) 'for each input field While lngStartPos > 0 'resets all input variables strInputHeader = "" strContetType = "" strInputContent = "" strInputName = "" strFileName = "" strFilePath = "" isFile = false 'skip row separation lngStartPos = lngStartPos + Len(strIDInput) + 2 'gets current input header strInputHeader = Mid(strByteToString, lngStartPos, InStr(lngStartPos, strByteToString, vbCrLf) + 2 lngStartPos) 'skip input header lngStartPos = lngStartPos + Len(strInputHeader) 'gets content type if any strContetType = Mid(strByteToString, lngStartPos, InStr(lngStartPos, strByteToString, vbCrLf) + 2 lngStartPos) - 114 - 'skip content type lngStartPos = lngStartPos + Len(strContetType) If Len(strContetType) > 2 Then lngStartPos = lngStartPos + 2 'gets content input value strInputContent = Mid(strByteToString, lngStartPos, InStr(lngStartPos, strByteToString, strIDInput) - 2 lngStartPos) 'skip content type lngStartPos = lngStartPos + Len(strInputContent) + 2 'gets input header fields 'gets "name" field lngFieldStartPos = InStr(1, strInputHeader, "; name=", vbTextCompare) + 8 strInputName = Mid(strInputHeader, lngFieldStartPos, InStr(lngFieldStartPos, strInputHeader, Chr(34)) lngFieldStartPos) lngFieldStartPos = 0 'gets "filename" field if exists lngFieldStartPos = InStr(1, strInputHeader, "; filename=", vbTextCompare) If lngFieldStartPos > 0 Then lngFieldStartPos = lngFieldStartPos + 12 strFilePath = Mid(strInputHeader, lngFieldStartPos, InStr(lngFieldStartPos, strInputHeader, Chr(34)) lngFieldStartPos) if len(strFilePath)>0 then strFileName = Mid(strFilePath, InStrRev(strFilePath, "\", Len(strFilePath)) + 1) strFilePath = Mid(strFilePath, 1, InStrRev(strFilePath, strFileName, Len(strFilePath)) - 1) end if End If lngFieldStartPos = 0 'from this point we have all data If strFileName <> "" Then isFile = True else isFile = False end if 'create the input object with current values Set objInput = new clsInput objInput.name = strInputName objInput.isFile = isFile objInput.content = strInputContent objInput.size = len(strInputContent) objInput.fileName = strFileName objInput.filePath = strFilePath 'append just created input object to "inputs" collection Set inputs(strInputName) = objInput Set objInput = Nothing 'go to next input value lngStartPos = InStr(lngStartPos, strByteToString, strIDInput) 'check if input separator is the last If Mid(strByteToString, lngStartPos + Len(strIDInput) + 1, 1) = "-" Then lngStartPos = 0 Wend bytArray = "" UpLoad = bytecount End Function </SCRIPT> - 115 - Script per la gestione degli upload <% 'setto la variabile come dimensione del file bytecount = UpLoad() 'se viene premuto il tasto Invia il file avrà una dimensione maggiore di 0 If bytecount > 0 then 'controllo se il file già esiste fileOverwrite = False 'creo gli oggetti necessari alla gestione dei file Set fso = Server.CreateObject("Scripting.FileSystemObject") file = Server.MapPath("./") & "/filear/" & inputs("uploadFile").fileName If fso.FileExists(file) then 'imposto la variabile a True, la utilizzerò dopo per bloccare l'upload fileOverwrite = true Response.Write "<p>Il file " & inputs("uploadFile").fileName &_ " eiste! Modificare il nome del file per effettuarne l'upload<p>" End If 'Distruggo gli oggetti creati e libero le risorse Set fso = nothing Set file = nothing 'se la stringa immessa nel form è effettivamente un file ‘e non si sta effettuando un overwrite allora eseguo l'upload If inputs("uploadFile").isFile = True and fileOverwrite = False then 'salvo il file nella directory definita retErr = inputs("uploadFile").saveFileAs(Server.MapPath("./") & "/filear/", "") %> <p>Il file <% =inputs("uploadFile").fileName %> è stato inviato correttamente<p> <% End If %> <% 'se non è stato scelto nessun file If inputs("uploadFile").fileName="" then Response.Write "<p>Non è stato scelto nessun file<p>" End If %> <!--form per upload--> <form action="" method="post" encType="multipart/form-data" name="Upload"> <INPUT name="uploadFile" type="file"><br> <INPUT type="submit" value="Invia" name="Invia"> <script>document.Upload.Invia.focus();</script> </form> <!--form per upload--> <% Else 'rigenerò il form di immissione del file poichè essendo una form "rientrante" ‘la gestione delle eccezoni mi fa ritornare alla pagina stessa %> <!--form per upload--> <form action="" method="post" encType="multipart/form-data" name="Upload"> <INPUT name="uploadFile" type="file"><br> <INPUT type="submit" value="Invia" name="Invia"> <script>document.Upload.Invia.focus();</script> - 116 - </form> <!--form per upload--> <% End If %> - 117 - Script per la pagina di visualizzazione e download dei file <p>I file disponibili sono:<p> <% 'creo gli oggetti necessari alla gestione dei file Set objFso = Server.createObject("Scripting.FileSystemObject") Set objFolder = objFso.GetFolder(Server.MapPath("./") & "/filear/") Set objFiles = objFolder.Files 'scorro tutti i file nella cartella selezionata For Each strFile in objFiles %> <table border="0" width="700"> <tr> <!--nome del file---> <td width="300"><a href="filear/<% =strFile.Name %>"><% =strFile.Name %> </a></td> <!--tipo del file---> <td width="300"> <% 'verifico se l'estensione del file è cry e specifico che si tratta di un file cifrato If Right(strFile.Name,3)= "cry" then Response.Write "File crittografato" Else Response.Write strFile.type End If %> </td> <!--dimensione del file---> <td width="100" align="right"> <% 'gestisco le dimensioni dei file in modo da farle visualizzare come multipli di byte If strFile.Size<1024 then Response.Write(strFile.Size) & "byte" ElseIf strFile.Size<1048576 then Response.Write(Round((strFile.Size/1024),2)) & "KB" Else Response.Write(Round((strFile.Size/1048576),2)) & "MB" End If %> </td> </tr> <% Next 'Distruggo gli oggetti creati e libero le risorse Set objFso = Nothing Set objFolder = Nothing Set objFiles = Nothing %> </table> - 118 - Script per il logout <% 'cancello il contenuto di ogni variabile Sessione Session.Contents.RemoveAll() 'elimino ogni Sessione aperta Session.Abandon %> - 119 - BIBLIOGRAFIA Kris Jamsa (2002) “Hacker Proof, Sicurezza in rete Seconda Edizione”, McGrawHill D.L. 30 giugno 2003, n. 196 “Codice in materia di protezione dei dati personali” Gazzetta Ufficiale n. 174 del 29 luglio 2003 – Supplemento Ordinario n. 123 D.P.R. 10 novembre 1997, n. 153 “Regolamento recante criteri e modalità per la formazione e la trasmissione con strumenti informatici e telematici, a norma dell’articolo 15, comma 2, della legge 15 marzo 1997, n. 59 Gazzetta Ufficiale 13 marzo 1998, Serie Generale, n. 60 Dizionario Treccani http://www.treccani.it Philippe Oechslin “Making a Faster Cryptanalytic Time-Memory Trade-Off” http://lasecwww.epfl.ch/pub/lasec/doc/Oech03.pdf RSA Laboratories “Public-Key Cryptography Standards (PKCS)” http://www.rsa.com/rsalabs/node.asp?id=2124 Specifiche tecniche sul formato dei file .bmp e .wav http://www.wotsit.org/ Specifiche sui protocolli http e https www.w3.org/Protocols Vulnerabilità delle applicazioni web http://www.owasp.org/ - 120 -