Siguranța parolelor

De mult timp, accesul în anumite locuri este limitat (din diverse motive). La început, "paznicul" lăsa doar pe cine trebuie; se presupunea că îi cunoaște pe "cei aleși". La un moment dat a fost nevoie ca accesul să fie permis și unor necunoscuți; au apărut cuvintele secrete care trebuia spuse, scrisorile de recomandare sau întregi conversații care trebuia purtat într-un anume fel. Lucrurile au evoluat și am ajuns acum la amprente, scanări ale retinei, teste ADN etc.

În ciuda evoluției securității, apar destul de des informații legate de breșe de securitate. Uneori parolele utilizatorilor au fost ghicite fiindcă au fost prost alese; alteori, acestea au fost furate deoarece erau păstrate într-un mod nesigur.

Trebuie să remarcăm faptul că în lumea digitală de acum, ambele părți au responsabilități. Utilizatorii trebuie să se asigure că parolele nu sunt cunoscute de alții, iar furnizorii de servicii trebuie să poată verifica parolele fără să existe riscul ca acestea să ajungă să fie cunoascute de alții. Niciuna din părți nu își poate face datoria singură; degeaba un utilizator alege o parola complicată și are grijă de ea, dacă furnizorul de serviciu o păstreaza ca text simplu, accesibil tuturor; de asemenea, degeaba furnizorul de servicii are o schemă complicată prin care condifică parolele astfel încât acestea să fie cât mai greu de aflat, dacă utilizatorul a ales o parolă simplă pe care a scris-o pe prima pagină a carnețelului și a lăsat foaia la vedere.

Ca programatori, nu ne ocupăm de prima situație; trebuie să presupunem că utilizatorul face eforturi minime pentru a-și proteja parola, dar trebuie să ne asigurăm că noi ne facem treaba bine. Nu vom intra în detalii despre cum ar trebui utilizatorii să își aleagă parolele, cât de des să le schimbe, cum să le țină minte etc.

De asemenea, nu ne ocupăm acum de metode de identificare care nu sunt bazate pe parole text. Amprentele, retina sau dispozitivele electronice nu sunt încă utilizate pe scară largă. Presupunem că utilizatorul trebuie să trimită un text și noi trebuie să protejăm acel text.

Recuperarea parolelor

Prima problemă care apare este aceea că utilizatorii își uită parolele și trebuie să avem un mecanism prin care să le poată recupera. Sunt diverse metode... Important este faptul că nu trebuie neapărat să recuperăm parola, ci să asigurăm din nou accesul utilizatorului. Așadar, nu trebuie nepărat să păstrăm parola propriu-zisă. Dacă păstrăm acea parolă cumva și vrem să o trimitem utilizatorului, riscul ca o persoană neautoriată să acceseze acea parolă este mai mare. În practică, probabil ați observat că se folosesc metode alternative de autentificare care permit apoi utilizatorului să aleagă o nouă parolă; câteva metode utilizate acum sunt SMS-uri, e-mail-uri, convorbiri telefonice, întrebări secrete etc. dacă atunci când încercăm să recuperăm parola ni se trimite exact parola, trebuie să ne punem deja întrebări serioase legate de securitatea datelor noastre.

Păstrarea parolelor

Am văzut că nu trebuie neapărat să păstrăm parolele, ci este suficient să găsim o modalitate alternativă prin care să permitem accesul dacă parola este pierdută. Așadar, trecem repede peste parolele stocate ca text simplu; nu se face așa ceva... Putem să le criptăm și să le descriptăm după caz (atunci când user-ul vrea să le recupereze), dar nu este suficient; dacă user-ul primește înapoi textul simplu, acesta poate fi interceptat. Trebuie să ne asigurăm că o parolă înterceptată este inutilă.

Hash criptografic unidirecțional

Interesantă denumire... Practic, vrem să transformăm un text într-un șir de caractere (hash) astfel încât textul original să fie greu sau aproape imposibil de determinat dacă este cunoscut hash-ul. Ideea este ca o persoană neautorizată care interceptează hash-ul să nu-l poată utiliza. Serverul va stoca doar hash-ul; utilizatorul va trimite parola și clientul va determina hash-ul pe care îl va trimite spre server. Pentru a permite accesul, vor fi comparate cele două hash-uri. Hash-ul nu ar trebui să poată fi trimis direct; deci este inutil pentru o persoană neautorizată dacă nu se poate recupera textul original.

Există mai multe metode de a genera astfel de hash-uri. Cele mai cunoscute sunt MD5 și SHA1. O caracteristică a lor este faptul că, deși în textul original s-ar modifica un singur bit, hash-ul ar arăta complet diferit. Să vedem câteva exemple:

GInfo și ginfo diferă printr-un singur bit, dar hash-urile sunt foarte diferite, atât în cazul MD5, cât și în cazul SHA1.

Impresionant, nu? Dar, nu este suficient pentru parole; mai ales dacă utilizatorii aleg parole scurte. Să vedem de ce...

Problema acestor codificări este că ele sunt unice și algoritmul după care sunt determinate este cunoscut. Dacă vedem 12ad8d6583abff6bfa52906cc05834fc este destul de greu să ne dăm seama că înseamnă GInfo. Dar, dacă am calculat odată hash-ul pentru GInfo și vedem undeva un hash identic, știm că parola este GInfo. În teorie, poate fi alta fiindcă funcția nu e bijectivă. Dar, cum și clientul și serverul lucrează cu hash-uri, putem folosi GInfo.

Veți spune că este puțin probabil să întâlnim parole pe care le știm deja ca să putem compara hash-urile. Nu e chiar așa. În principiu, pentru fiecare caracter dintr-o parolă avem 64 de variante (sunt folosite cifre, litere și câteva caractere speciale); sunt în realitate puțin mai multe, dar am aproximat cu 26 ca să simplificăm calculele care urmează. Dacă avem șapte caractere într-o parolă, avem 242 combinații posibile. Avem nevoie de 16 sau 20 de bytes pentru un hash (observați că avem 32 de cifre hexazecimale pentru MD5 și 40 pentru SHA1). Pentru cele 242 combinații am avea nevoie de 64 TB sau 80 TB. Deja pare fezabil; dacă avem un hash ale unei parole care conține cel mult șapte caractere, putem afla parola. Ne putem imagina acum de ce una dintre recomandările pentru alegerea parolelor este ca acestea să conțină cel puțin 8 caractere.

Dar, lista noastră de hash-uri este simplistă. Există metode mai performante; de exemplu, așa numitele rainbow tables ne permit să găsim orice parolă pornind de la hash-ul SHA1 dacă aceasta este formată din cel mult nouă caractere alfanumerice și avem nevoie doar de 864 GB pentru acest lucru. De data aceasta ne putem imagina motivele pentru care ni se sugerează să adăugăm caractere speciale în parole.

Puțină sare...

Am văzut de unde vin anumite recomandări pentru parole. Dar, nu ne putem baza pe utilizatori; ei nu vor alege neapărat parole sigure. De aceea, adăugăm puțină sare... Sarea este, de fapt, o secvență de caractere pe care o adăugăm parolei, astfel încât hash-ul să fie diferit. Această secvență este stocată împreună cu hash-ul; este recomandat ca secvența să fie diferită pentru fiecare parolă în parte (în principiu este generată aleator de fiecare dată când este aleasă sau modificată o parolă). Astfel, chiar dacă doi utilizatori ar alege aceeași parolă (fie ea și simplă), hash-urile lor ar fi diferite.

Dar, problema nu este încă rezolvată complet. Aceste hash-uri sunt calculate foarte rapid. Dacă adăugăm sare, este mai greu, dar nu e sigur. Se pot genera milioane de hash-uri pe secundă; riscul este în continuare mare. Nu putem crea tabele care să ne permită să găsim repede orice parolă, dar putem "ataca" parole individuale.

Să o luăm mai încet

Ce-ar fi dacă am încetini calcularea hash-urilor? Când calculăm un hash, ne ia ceva mai mult timp. Dar, celor care încearcă să construiască tabele sau să încerce diverse variante, le ia mult mai mult timp. Ei nu mai pot să genereze milioane de hash-uri pe secundă. Există mai multe metode mai lente care calculează hash-uri; Bcrypt, Scrypt sau PBKDF2 sunt câteva dintre ele. Pentru ca totul să fie perfect, putem adăuga din nou sare...

Astfel de algoritmi au un parametru numit cost. Pentru determinarea hash-ului, se iterează de un număr de ori care depinde de acest parametru. Pe măsură ce puterea de calcul crește, putem mări și noi costul astfel încât construcția hash-urilor să fie din ce în ce mai dificilă.

Concluzii

Nu avem voie să stocăm parolele în format text. Putem folosi un hash; e recomandabil să adăugăm și niște sare. E și mai bine dacă hash-ul nu poate fi calculat foarte rapid...