Mokabyte

Dal 1996, architetture, metodologie, sviluppo software

  • Argomenti
    • Programmazione & Linguaggi
      • Java
      • DataBase & elaborazione dei dati
      • Frameworks & Tools
      • Processi di sviluppo
    • Architetture dei sistemi
      • Sicurezza informatica
      • DevOps
    • Project Management
      • Organizzazione aziendale
      • HR
      • Soft skills
    • Lean/Agile
      • Scrum
      • Teoria della complessità
      • Apprendimento & Serious Gaming
    • Internet & Digital
      • Cultura & Società
      • Conferenze & Reportage
      • Marketing & eCommerce
    • Hardware & Tecnologia
      • Intelligenza artificiale
      • UX design & Grafica
  • Ultimo numero
  • Archivio
    • Archivio dal 2006 ad oggi
    • Il primo sito web – 1996-2005
  • Chi siamo
  • Ventennale
  • Libri
  • Contatti
  • Argomenti
    • Programmazione & Linguaggi
      • Java
      • DataBase & elaborazione dei dati
      • Frameworks & Tools
      • Processi di sviluppo
    • Architetture dei sistemi
      • Sicurezza informatica
      • DevOps
    • Project Management
      • Organizzazione aziendale
      • HR
      • Soft skills
    • Lean/Agile
      • Scrum
      • Teoria della complessità
      • Apprendimento & Serious Gaming
    • Internet & Digital
      • Cultura & Società
      • Conferenze & Reportage
      • Marketing & eCommerce
    • Hardware & Tecnologia
      • Intelligenza artificiale
      • UX design & Grafica
  • Ultimo numero
  • Archivio
    • Archivio dal 2006 ad oggi
    • Il primo sito web – 1996-2005
  • Chi siamo
  • Ventennale
  • Libri
  • Contatti

Nel numero:

99 settembre
, anno 2005

Autenticazione password crittografata

Una semplice applicazione Struts

Avatar
Jonathan diNardo
MokaByte

Autenticazione password crittografata

Una semplice applicazione Struts

Picture of Jonathan diNardo

Jonathan diNardo

  • Questo articolo parla di: Programmazione & Linguaggi, Sicurezza informatica

In questo articolo spiegherò come realizzare un sistema di autenticazione utilizzando le classi MessageDigest e SecureRandom per implementare un meccanismo di autenticazione basato sul Hashing delle password.

L‘autenticazione in breve

Il sistema che andrò ad illustrare è in larga parte utilizzato e simile in sistemi UNIX/LINUX . La password verrà  crittografata per mezzo di un algoritmo unidirezionale ovvero sarà  possibile criptare la password ma non decriptarla (quindi tornare indietro al testo in chiaro) e salvata sul database. In primo luogo:


  • Si registrano i dati dell‘utente su db password crittografata compresa.
  • Al momento della login sistema il prende la password (testo in chiaro), che ha inviato l‘utente ed esegue la crittografia ottenendo un hash.
  • Si confronta il contenuto dell‘hash con quello relativo all‘account-utente presente sul db. Se corrispondono l‘utente accede al sistema. Per crittografare la password abbiamo bisogno di una chiave che non dovrebbe essere statica ma cambiare spesso in modo che il valore dell‘hash cambi ad ogni passaggio.

Il salt

Il salt è un array di byte generato in modo casuale che occorre per criptare la password in chiaro e di conseguenza ottenere l,‘hash. La concatenazione dall‘hash + salt andrà  a formare la password crittografata che verrà  salvata sul db. La password sarà  composta pressappoco come nella figura 1.

Naturalmente il contenuto crittografato nella norma è più lungo ma questo vuole suolo essere un esempio. Il contenuto della figura 1 è composto quindi da:

  • un prima parte denominata hash che è sostanzialmente la password in chiaro crittografata, ovvero il risultato dell‘agoritmo di crittografia.
  • una seconda (salt) che contiene la chiave utilizzata per crittografare la password in ingresso ed ottenere l‘hash.

Importante: Deve essere nota la lunghezza del salt che nell‘esempio è composto da soli 9 byte. Solitamente il salt ha una lunghezza sempre fissa per poter essere estratto dall‘intero array. Nel nostro caso la lunghezza del salt sarà  di 5 + la lunghezza della password in chiaro.
Quando l‘utente prova ad effettuare la login invia username e password: Il sistema effettua i seguenti passaggi:

  • Cerca l‘utente sul db per mezzo dello username.
  • Estrae il record contenente informazioni dell‘account dell‘utente ma utilizzerà  per il momento solo la password crittografata presente(hash + salt)
  • Estrae la chiave(salt): Per estrarre il salt bisogna ottenerne la lunghezza. Si somma a 5 la lunghezza della password in chiaro inviata al momento della login dall‘utente. Il numero ottenuto sono gli ultimi n byte del campo. Nello schema la password è di soli 4 caratteri perchè se sommiamo a 5 otteniamo un salt di 9 byte. Contando dall‘ultima posizione(20) arriviamo a 12. Da qui comincia il salt. Naturalmente una password di 4 caratteri è tutt‘altro che sicura ma questo vuole solo essere un esempio. Quindi
  • Si estrae l‘hash.
  • Prova a crittografare la password inserita dall‘utente ovvero viene generato un nuovo hash a partire dalla password in chiaro utilizzando il salt estratto in precedenza.
  • Controlla il nuovo hash generato se corrisponde a quello estratto dal db.

Se l‘autenticazione ha avuto esito positivo conosciamo il testo in chiaro della password inserita al momento della login. Ci comportiamo di conseguenza:

  • Generiamo un nuovo salt.
  • Prendiamo la password che ha inserito l‘utente (sappiamo che è quella giusta e che è quella in chiaro) e generiamo un nuovo hash utilizzando come chiave il nuovo salt generato.
  • Concateniamo hash + salt ed otteniamo una nuova password crittografata. Aggiorniamo il campo sul db con questo valore.

Secondo questo sistema sappiamo che:

  • la password in chiaro non viene salvata mai da nessuna parte, ma utilizzata solo in ingresso al momento della login.
  • il contenuto crittografato sul db della password cambia ad ogni login perché utilizziamo una chiave che cambia ad ogni autenticazione.

La registrazione dell‘utente.

Questa fase non meno importante in quanto permette, assieme alla creazione dell‘utente implica la generazione della password crittografata. In primo luogo quando utente vuole registrarsi gli viene richiesto di inserire i suoi dati anagrafici. Tra l‘altro l‘inserimento dellla password è previsto due volte. Il procedimento è simile come nell‘autenticazione: Si prendono le due password inserite, si genera uno solo salt per tutte e due. Dalle due password in chiaro applicando il salt si ottengono due hash. Se sono identici si concatena hash e salt per ottenere la password crittografa. Si crea l‘account sul db con tutti i dati.

Passiamo al codice:
Non mi dilungherò molto nell‘implementazione della funzionalità  in struts, quanto per classi dedicate alla funzionalità  di autenticazione.

LoginAction

Questa action di struts non fa altro che delegare ad un oggetto di tipo AuthenticationModule la funzionalità  di autenticazione passando i parametri di accesso dell‘utente. Il compito della login action è preparare i parametri di ingresso e gestire il risultato dell‘autenticazione per effettuare il forward delle varie pagine.

AuthenticationModule

Questa classe viene utilizzata al momento della login: Il costruttore accetta due parametri:

public AuthenticationModule(String username,String password)

Che sono in pratica lo username e la password in chiaro provenienti dalla request. Il metodo.

public UserBean login() throws Exception;

restituisce un oggetto UserBean se l‘autenticazione va a buon fine oppure rilascia un eccezione che può essere.

  • Un‘eccezione AuthenticationException.
  • Un‘eccezione generica java.lang.Exception.

Se abbiamo un eccezione del tipo AuthenticationException vuol dire che l‘autenticazione ha avuto esito negativo: Si verifica nel caso l‘utente non sia presente sul db oppure semplicemente l‘utente è presente ma la password è errata. E‘ comunque possibile gestire i due casi tramite un codice di errore.

AuthenticationUtil: Creazione della password

Questa classe viene utilizzata all‘interno della login ed è una classe di supporto. Ha due metodi il primo è:

public static byte[] createPassword(byte password[],byte oldPassword[])

Questo metodo genera una nuova password crittografata a partire da un testo. Il secondo parametro rappresenta la password registrata attualmente sul db. Questo secondo parametro servirà  per generare un nuovo salt in con la massima causalità . Anche nella fase di creazione di un utente viene utilizzato questo metodo ma è ovvio che per la creazione della prima password non si gestisce il secondo parametro.

Per prima otteniamo un istanza di messagedigest.

MessageDigest sha = MessageDigest.getInstance(ALGORITHMS_DIGEST);SecureRandom random = null;

Adesso controlliamo se ci è stata passata la vecchia password. Se non arriva generiamo direttamente il salt.

if (oldPassword==null) random = SecureRandom.getInstance(ALGORITHMS_RANDOM);

Se ci viene passata la vecchia password. Utilizziamo questa (come seme) per generare il nuovo salt.

if (oldPassword!=null) random = new SecureRandom(oldPassword); byte salt[]  = random.generateSeed(SALT_MIN_LEN + password.length);sha.reset();

A questo punto applico l‘algoritmo alla password in chiaro assieme al salt per ottenere l‘hash.

sha.update(password);sha.update(salt);byte encr[] = sha.digest(salt);

Creo un nuovo array con dimensioni pari a contenere l‘hash ed il salt che ho creato.

result = new byte[encr.length + salt.length]; 

Quindi copio prima l‘hash all‘inizio del nuovo array.

System.arraycopy(encr, 0, result, 0, encr.length);

Infine copio il salt in coda sempre nello stesso array.

System.arraycopy(salt, 0, result, encr.length, salt.length);return result; 

AuthenticationUtil: Verifica della password

public static boolean verify(byte password[],byte dbpassword[])

Il metodo verify, come suggerisce il nome, serve per confrontare un testo in chiaro con uno crittografato. Se crittografando il primo parametro (password in ingresso) ottengo un hash con contenuto identico al secondo parametro il metodo ha esito positivo. Come vedremo piu in dettaglio per criptare la password la funzione utilizzerà  il salt che è parte del contenuto del parametro dbpassword.
Per prima cosa inizializzo il digest,

sha = MessageDigest.getInstance(ALGORITHMS_DIGEST);sha.reset();

Adesso calcolo la posizione di inizio del salt nella password crittografata SALT_MIN_LEN è una costante intera che impostata 5. Sommo a questo la lunghezza della password in chiaro. Infine sottraggo alla lunghezza della password crittografata il totale ed ottengo la posizione del salt nell‘array.

int startAt =  dbpassword.length  - (SALT_MIN_LEN + password.length); if (startAt>0){

Istanzio l‘array che dovrà  contenere solo il salt.

byte salt[] = new byte[SALT_MIN_LEN + password.length];

Istanzio l‘array che dovrà  contenere solo l‘hash

byte enc[]  = new byte[startAt];

Copio soltanto la porzione che riguarda il salt nel nuovo array.

System.arraycopy(dbpassword,startAt,salt,0,salt.length);

Infine copio l‘hash nel nuovo array.

System.arraycopy(dbpassword,0,enc,0,enc.length);

Adesso provo a generare il nuovo hash con la passoword in chiaro(primo parametro)

sha.update(password);sha.update(salt);byte toBytePassword[] = sha.digest(salt);

confronto l‘hash generato dalla password in chiaro con quello che ho estratto prima e restituisco il risultato.

result = MessageDigest.isEqual(toBytePassword,enc);

Util

Questa classe di supporto viene utilizzata semplicemente per delle operazioni di conversione di formato.

public static String toString (byte[] bytes)

Converte un array di byte in una stringa esadecimale. La stringa risultante è un numero in formato esadecimale. Nella stringa generata un byte occuperà  sempre due caratteri.

public static byte[] toByte (String hex)

Viceversa questa funzione converte un stringa in formato esadecimale in un array di byte.
La password crittografata dell‘utente verrà  infatti salvata sul db in formato esadecimale (utilizzeremo per questo campo un tipo VARCHAR) e viceversa convertita in byte durante la lettura. La scelta di scrivere in formato esadecimale sul db è solamente una scelta di comodo si potrebbe scrivere ad esempio anche in formato BASE64.

RegisterUser

Questa classe si utilizza utilizzata semplicemente per registrare un utente. Il costruttore

RegisterUser(UserBean userBean,byte password[])
  • Accetta un oggetto UserBean.
  • La password già  crittografata

UserBean è appunto un bean che dovrebbe contenere tutte informazioni relative all‘utente che si sta registrando (Cognome,Nome,indirizzo etc..), per semplicità  il bean contiene solo nome,cognome e idutente.
Infine il metodo:

public boolean register() throws Exception

che crea fisicamente il record contenente l‘informazioni dell‘utente. Il metodo restituisce un booleano. Se restituisce true la registrazione è avvenuta con successo; se restituisce false significa che esiste già  un utente con tale id.
Mi connetto al db utilizzando il datasource.

InitialContext initCtx = new InitialContext();DataSource mysqlds= (DataSource)initCtx.lookup(Constants.JNDI_DATA_SOURCE_NAME);connection =  mysqlds.getConnection();statement  = connection.createStatement();

Converto la password già  crittografata che ho passato al costruttore in esadecimale per mezzo della classe Util.

String dbpassword = Util.toString(password);

Prendo tutte le informazione dal bean (cognome,nome etc)

String nome = userBean.getNome();

..

Effettuo una ricerca per vedere se non esiste già  un utente con questa username.

String queryUser="SELECT COUNT(*) AS CONTEGGIO FROM USERS WHERE USERNAME=‘" + username + "‘";resultset = statement.executeQuery(queryUser);if (resultset.next()) {result = resultset.getInt("CONTEGGIO")==0;try{resultset.close();}catch(Exception er){};} if (result) {

Se l‘utente non esiste eseguo la insert sul db (password crittografata compresa).

String queryInsert="INSERT INTO USERS (USERNAME,COGNOME,NOME,PASSWORD) VALUES(‘" + username + "‘,‘" + cognome + "‘,‘" + nome + "‘,‘" + dbpassword + "‘)";statement.executeUpdate(queryInsert);}

Il metodo login di AutenticationModule

public UserBean login() throws AuthenticationException,Exception{...

Accedo al db per ricavare i dati dell‘utente. Cerco in base alla username

InitialContext initCtx = new InitialContext();DataSource mysqlds= (DataSource)initCtx.lookup(Constants.JNDI_DATA_SOURCE_NAME); connection =  mysqlds.getConnection();statement  = connection.createStatement();resultset = statement.executeQuery("SELECT USERNAME,NOME,COGNOME,PASSWORD FROM   USERS WHERE USERNAME=‘" + username + "‘");

Controllo se esiste un record per l‘utente.

if (resultset.next()){

Ricavo l‘intera password dal db. La password è in formato esadecimale. Quindi utilizzo la classe Util per convertire in in un array di byte

dbpassword = Util.toByte(resultset.getString("PASSWORD"));

Adesso utilizzo la classe AuthenticationUtil ed il metodo verify per controllare se la password inserita dall‘utente corrisponde a quella sul db.

result = AuthenticationUtil.verify(password,dbpassword);

l‘autenticazione ha esito positivo carico i dati anagrafici dell‘utente.

if (result){userBean = new UserBean();userBean.setCognome(resultset.getString("COGNOME"));...

Quindi creo una nuova password (salt ed hash) ed effettuo l‘aggiornamento del campo sul db (nuova password).

String newPassword = Util.toString(AuthenticationUtil.createPassword( password,dbpassword));String updateQuery="UPDATE USERS SET PASSWORD=‘" + newPassword + "‘ WHERE USERNAME=‘" + username + "‘";statement.executeUpdate(updateQuery);

Se invece la password non corrisponde a quella sul db preparo il codice di errore (AuthenticationException).

}else errorcode = AuthenticationException.INVALID_PASSWORD;

Nel caso in cui l‘utente è stato trovato sul db preparo un codice di errore differente.

}else{errorcode = AuthenticationException.USERS_NOT_EXIST;}

Chiudo comunque tutte le connessioni.

}finally{try{resultset.close() ;resultset  = null;}catch(Exception ee){};try{statement.close() ;statement  = null;}catch(Exception ee){};try{connection.close();connection = null;}catch(Exception ee){};}

Controllo se esiste un errore di autenticazione. Se esiste lancio l‘eccezione. Altrimenti vado avanti e restituisco il bean dei dati utente.

if (errorcode>-1) throw new AuthenticationException(errorcode);return userBean;

Conclusioni

La funzionalità  di autenticazione è completa ed il codice di esempio prevede l‘utilizzo di struts. E‘ molto semplice riadattare il codice per utilizzarla con una normale applicazione web. Per sviluppo ed il testing dell‘applicazione ho utilizzato:

  • Apache Tomcat 4.1.31
  • MySql 4.0.24
  • Junit
  • Jakarta Struts

Nella seconda parte illustrerò come controllare, per mezzo di un‘impronta digitale, la sessione utente. Osserveremo come sia possibile utilizzare l‘impronta per controllare anche l‘accesso concorrente dello stesso account.

Riferimenti bibliografici

Oaks Scott “Java Security, Second Edition”, Apogeo, 2001

“Programming Jakarta Struts”, Hoepli, 2003

Avatar
Jonathan diNardo
Facebook
Twitter
LinkedIn
Picture of Jonathan diNardo

Jonathan diNardo

Tutti gli articoli
Nello stesso numero
Loading...

Soluzioni Oracle per la scalabilità

Come rendere scalabili le applicazioni Oracle

Il nome della cosa

Sun rivede e uniforma i nomi delle proprie piattaforme

Spring Web Flow in un esempio pratico

Il Jug Avis Web (aka MagicBox)

Il networking in Java

II parte: il TCP e i socket

Validare il codice Java

Preveniamo lo spaghetti code

Multimedialità su J2ME

IV parte: riproduzione, registrazione e transcodifica video

Integrazione di applicazioni Enterprise

Parte VIII: l‘Enterprise Integration Component Server di Librados

MokaCMS – Open Source per il Web Content Management

VII parte: Da XML ad PDF utilizzando XSLT e FO (seconda puntata)

Pratiche di sviluppo del software

VI - Code Coverage

Nella stessa serie
Loading...

Il dilemma del prigioniero

Un “gioco serio” per comprendere la cooperazione

Accessibilità in team di prodotto: sfide, normative e best practice

II parte: Analisi di un caso reale

Adattare l’agilità ai contesti: una chiave di lettura

I parte: Un caso di studio con le sue peculiarità

Accessibilità in team di prodotto: sfide, normative e best practice

I parte: Cosa è l’accessibilità e perché implementarla

Il web al tempo della GEO (Generative Engine Optimization)

II parte: Strategie per strutturare i contenuti

Un backlog non tanto buono

II parte: Caratteristiche e ruolo del backlog.

FIWARE: Open APIs for Open Minds

V parte: Implementazione del sistema di ricarica

Il web al tempo della GEO (Generative Engine Optimization)

I parte: Struttura e ricerca delle informazioni

Un backlog non tanto buono

I parte: Un progetto con qualche difficoltà

DDD, microservizi e architetture evolutive: uno sguardo d’insieme

X parte: Il ruolo del Software Architect

FIWARE: Open APIs for Open Minds

IV parte: Sistema di ricarica intelligente per veicoli elettrici

Tra Play14 e serious gaming

Un ponte tra gioco e apprendimento

DDD, microservizi e architetture evolutive: uno sguardo d’insieme

IX parte: Event Sourcing is not Event Streaming

FIWARE: Open APIs for Open Minds

III parte: Tecnologie e implementazione

Agilità organizzativa

II parte: Qualche caso d’esempio

Agilità organizzativa

I parte: Individui e interazioni nelle aziende moderne

FIWARE: Open APIs for Open Minds

II parte: Generic Enablers per costruire ecosistemi smart

Intelligenza artificiale e industria

Riflessioni sull’uomo e sulla macchina

Effetto Forrester e dinamiche dei sistemi di produzione

La storiella di una birra per comprendere il Lean

DDD, microservizi e architetture evolutive: uno sguardo d’insieme

VIII parte: La filosofia dell’architettura del software

Digital revolution: trasformare le aziende in ecosistemi digitali

XVIII parte: Una piattaforma comune a tutti gli eventi

Scene dalla “neolingua”

Panoramica semiseria dell’incomunicabilità aziendale

Autenticazione schede elettorali… lean!

Simulazione lean nella gestione di un seggio

FIWARE: Open APIs for Open Minds

I parte: Fondamenti e architettura

Mokabyte

MokaByte è una rivista online nata nel 1996, dedicata alla comunità degli sviluppatori java.
La rivista tratta di vari argomenti, tra cui architetture enterprise e integrazione, metodologie di sviluppo lean/agile e aspetti sociali e culturali del web.

Imola Informatica

MokaByte è un marchio registrato da:
Imola Informatica S.P.A.
Via Selice 66/a 40026 Imola (BO)
C.F. e Iscriz. Registro imprese BO 03351570373
P.I. 00614381200
Cap. Soc. euro 100.000,00 i.v.

Privacy | Cookie Policy

Contatti

Contattaci tramite la nostra pagina contatti, oppure scrivendo a redazione@mokabyte.it

Seguici sui social

Facebook Linkedin Rss
Imola Informatica
Mokabyte