[da testare] Libreria per autenticazione sicura

Ciao,
visto che ogni tanto le notre applicazioni sono esposte via internet, è bene proteggerle con una password.
Però mi sono accorto che spesso le guide fanno "viaggiare" le password in chiaro, o peggio le "criptano" in base64, facendo intender che sia più sicuro.

Inutile sottolineare che non sia così. E che il nostro povero arduino NON regge la criptazione SSL, e quindi niente HTTPS.

Quindi il sistema usato è molto semplice:
Un ipotetico client si collega, eventualmente inviando un userID.
Il server gli assegna un userID (se non presente) e un codice, e li spedisce al client (in chiaro).

Attenzione, il codice non è testato.
Lo metto quì per vedere a quanti interessa, domani sera spero di riuscire a fare qualche test.

la libreria necessita anche della libreria ArduinoMD5 che potete scaricare qui GitHub - tzikis/ArduinoMD5: The first easily-embeddable MD5 library for Arduino

unsigned long code1 = auth.askToken(userID, userPSW); //userPSW è la password CORRETTA per l'utente, che arduino deve conoscere

il client concatena (attenzione all'odine, e senza i +) userID+codice+password dopo di che fà l'MD5 della stringa ottenuta e la spedisce al server, oltre che il suo userID

Il server controlla che la stringa md5 sia valida

boolean isValid = auth.check(userID, responseMd5);

ATTEZIONE: il codice NON sarà più valido

a questo punto, il server potrebbe generare un nuovo codice chiamando di nuovo askToken() (magari con un userID fittizio/random per mischiare un pò le acque) e mantenere così attiva una sessione "sicura"; anche se l'attaccante intercetta il traffico, nel peggiore dei casi sarà in grado di eseguire UNA sola chiamata spacciandosi per noi. Notare che la debollezza dell'MD5 (le collisioni) sono aggirate dalla generazione di un codice nuovo ad ogni chiamata.

Uno UserID può avere più di un token valido alla volta, e di default un token "scade" dopo 10 minuti. Per settare un valore di timeout arbotrario IN MILLISECONDI usare

AuthManager auth(10 * 1000UL); //in questo caso timeout di 10 secondi

AuthManager.cpp.zip (1.26 KB)

Aspe, spiega.
Chi fornisce le password?

Parli di codici non più validi, ti tieni una lista di codici scaduti?

Perché codici e password? Che differenza c'è nella tua idea fra il primo ed il secondo?

Scusa le domande, ma mi interessa capire bene :stuck_out_tongue:

Leo è una tecnica che uso molto spesso anche io quando non è richiesta un'altissima sicurezza ...

In particolare io uso la seguente tecnica :

  1. il client chiede al server un codice che varrà solo quella volta (è generato random)
  2. il client conosce una sua parola chiave proprietaria che conosce anche il server
  3. il client prepara il messaggio che deve mandare la server dopo di che ci aggiunge in punti che solo lui e il server conoscono (es. in testa e in coda, in mezzo, ...) il codice e la parola chiave e del tutto calcola l'MD5
  4. il client trasmette al server il messaggio e l'MD5 NON il codice (tanto il server lo ha generato lui) e NON la parola chiave che il server deve sapere.
  5. Il serve riceve calcola allo stesso modo l'MD5 e li confronta ... e così autentica il messaggio

Questo ha due vantaggi ...

  1. per chi NON ha accesso al SW, il codice che cambia ogni volta sicuramente complica la vita
  2. per chi HA accesso al codice ... comunque non ha la parola chiave e quindi comunque non è in grado di calcolare l'MD5

Il punto 2 è importante se "distribuisci" il prodotto e quindi, chi lo compra, può vedere il codice (la mia parte server è scritta in PHP ... vero che l'ho offuscata ... ma anche quello spesso lascia il tempo che trova) ma ogni cliente usa la SUA parola chiave che gli altri non conoscono :wink:

Oh, ripeto, è per dove non serve una grandissima sicurezza ... altrimenti si passa a generatori HW di numeri casuali e di OTP :slight_smile:

Guglielmo

esatto, sono "Token one time" generati a partire da una password comune decisa a priori, ed un codice generato dal server (per evitare che un client truffaldino riutilizzi una combinazione MD5+codice sniffata, nel mio caso visto che ilreinvio del codice da client a server non è necessario bisogna sniffare le 2 richieste fatte dal client, la prima in cui il server da il codice, la seconda in cui il client invia l'MD5)

poi la sicurezza è quella che è, ci mancherebbe, ma almeno la password è al sicuro da "sniffatori", per esempio se si usa la rete aziendale o wifi NON nostre.. (uso la millis() per generare codici! quindi se hai pazienza ~50 giorni e hai sniffato a priori.. certo se componessi il codice con DUE long...)

In teoria il codice è da usare in combinazione con una libreria che interpreta le richiete HTTP (anzi, quasi REST! GET, POST, PUT, DELETE), la rilascio a breve... datemi un'oretta :grin:

Ah, ok. Sono protocolli di scambio di autenticazione.
Con la spiegazione di Guglielmo ne ho avuto la conferma.
Non avevo capito bene dalle spiegazioni iniziali :sweat_smile:

lesto:
(uso la millis() per generare codici! quindi se hai pazienza ~50 giorni e hai sniffato a priori.. certo se componessi il codice con DUE long...)

Potresti inserire sul timer 2 una ISR che ti incrementa un altro long, magari con frequenza differente rispetto a quella usata sul timer 1.
Oppure, altra soluzione, potresti usare un long long. Il compilatore supporta questi numeri a 64 bit (ricordi le prime versioni della swRTC?), anche se, dovendoli gestire via software, si appesantisce un pò il codice. Ma per avere giusto un timestamp, puoi pagare il dazio di quei 2/300 byte in flash di più :wink:

posso usare il long long, ma sarebbe complesso visto che per ora faccio codice = millis(), dovrei creareun sistema ad hoc... un contatore di overflow di millis(), per esempio :slight_smile: se millis() < lastMillisUsed allora il secondo codice++

Un sistema ad hoc è banale. Ti crei una piccola routine ISR e la agganci al timer 2, impostato per overflow da 1 ms. Lì dentro metti un counter++, con counter dichiarato unsigned long long. Stop. :wink:
Sono 5 righe di codice, tra setup del codice e ISR.

ma perdo un timer..

ps. ho un problema grosso. Se metto una String dentro una struttra sembrano nascere problemi di RAM.

Il che è strano, in teoria la String ha dimensione fissa, tranne il PUNTATORE alla stringa, che però essendo chissà dove non dovrebbe dare probelmi. l'uso di ram credo esssere molto basso, ora provo

Non so se la perdita di un timer sia un problema oppure no, questo devi giudicarlo tu.
Un'alternativa ci sarebbe.... hackeri il core e metti nella ISR del timer 0 un'aggiunta al codice originale con cui incrementi una long long. E ti fai una millis2.

Oppure usi il watchdog ma hai un intervallo minimo di soli 16 ms

nono, l'idea è che deve essere semplicee senza side-effect.

Se prioprio devo, uso il sistema del +1 se rilevo che millis() è < dell'ultimo valore usato. Se ci pensianchese perdo un overflow, il peggio che può capitare è un token non scaduto (se è generato poco prima dell'overflow, e il secondo overflow siè verificato da poco) main tal caso non è un grossissimo problema REALE, certo uno smacco dal puntodi vista teorico.

Mi potete dare una mano a scrivere il primo post in modo che sia "capibile"? :grin:

Anche per la stesura del codice, ho preferito eliminare brutture* ma fare in modo che fosse un esercizio di stile.
*(tranne l'uso di una String, ma rimedierò se necessario)

Lo faccio perchè così sperodi invogliareun pò a leggere il codice, certo non è un codice da novellino (doppi puntatori, liste, *alloc)

comunque ho appena testato e finito il parser HTTP, ora lo linko (anche questo un bel esercizio di stile e macchina a stati)

Non puoi usare microseconds() come secondo long e lo concateni con millis()?

esiste una probabilità di collisione più alta che facendo il +1 ad ogni overflow rilevato,in oltre non aggiunge niente di più del +1.Certo è più semplice :slight_smile:

Allora creati una routine ISR che agganci al watchdog e che incrementa una variabile, stile millis.
Hai una risoluzione di 16 ms ma difficilmente avrai collisioni dato che il watchdog è clockato da un risuonatore a 128 kHz indipendente.
Se ricordi, nella mia libreria pRNG uso proprio le tolleranze di questo risuonatore e le differenze con il clock principale per leggere ad intervalli regolari il registro di un timer e prelevare entropia. In questo modo, anche all'avvio iniziale, ho sempre una differenza seppur minima.

La lib di Leo la trovi qui --> Nuova libreria pRNG - Software - Arduino Forum

seguo

ciao ... io usavo la richiesta su http 401 Digest Access Authentication che applica già l'MD5 la stringa a 32 char contiene l'username la passwor criptata in base ai paramentri impostati nella richiesta

i parametri sono
Digest realm="",
qop="
",
nonce="*",
opaque="
",
come evidenziato anche qui Digest access authentication - Wikipedia

nessuna libreria è necessaria in questo caso, senza incrementare il lavoro del micro, la sicurezza è sicuramente superiore del Base64, decriptare qulla sringa MD5 non penso sia così immediato però ... che ne dite ?

ciao

decriptare un MD5 è praticamente impossibile, dato che essendo un numero di byte fisso, perdi informazione. Quello che però è facile da fare è generare una collisione, ovvero valore che genera lo stesso MD5 di un altro. Quindi se usi il DAA solo con user+pass dovrebbe essere molto facile. Infatti poi è stato uaggiungo il "nouce", che non è altro che quello che io chiamo "code", ed anche un cnounce. Però vedo che il protocollo nonprevedere di registrare A CHI viene assegnato il "nounce", ne prevede una sua scadenza ma nemmeno una sua memorizzazione lato server, quindi un client malevolo in teoria potrebbe riutilizzare un "nouce" noto, se l'implementazione lato server è carente

Sicuramente l'uso di una libreria per il calcolo md5, e di una che faccia mantenga i nounce assegnati e ne verifichi la correttezza della risposta è necessaria, certo puoi farne a meno come puoi fare a meno di questa.

@leo72 + PaoloP: anche usando un timer esterno ABBASSI la probabilità ma non la escludi, a questo punto potrei semplicemente usare un unsigned int/long che contiene il numero di Token, ogni token generato aumenta di 1 il valore. Su un arduino oltre due miliardi di valori unici più la millis() danno un alta improbabilità di riuso e a costo quasi zero