Kako ispravno enkriptovati korisničke lozinke? | coolcao mala stanica
Kako ispravno enkriptovati korisničke lozinke?
Objavljeno 2018-07-24 | Kategorisano u Tehnički blog, Preuzeto
Ovaj članak je preuzet iz Kako ispravno enkriptovati korisničke lozinke?. Preveo Liu Zhiyong iz člana Defuse Security tima Salted Password Hashing - Doing it Right
Kao Web developer, često moramo da radimo sa korisničkim sistemom naloga, a najveći izazov je kako zaštititi korisničke lozinke. Često vidimo da se baze podataka korisničkih naloga često hakuju, zato moramo preduzeti neke mere da zaštitimo korisničke lozinke, kako bi izbegli nepotrebno curenje podataka. Najbolji način zaštite lozinki je korišćenje heširanja lozinki sa soljom (salted password hashing).
U vezi heširanja lozinki postoje mnoge debate i pogrešna shvatanja, možda zato što na internetu postoje mnoge netačne informacije. Heširanje lozinki je vrlo jednostavna stvar, ali mnogi ljudi prave greške. Ovaj članak će fokusirati na deljenje kako ispravno enkriptovati korisničke lozinke.
Važno upozorenje: Molim vas da odustanete od ideje o pisanju sopstvenog koda za heširanje lozinki! Zato što je ova stvar previše lako da se zakači. Čak i ako ste učili kriptografiju na fakultetu, trebalo bi da pratite ovo upozorenje. Svi moraju zapamtiti ovu tačku: ne pisite sopstvene algoritme za heširanje enkripcije! Rešenja za čuvanje lozinki već postoje, zato koristite phpass, ili implementacije u PHP-u, C#-u, Javi i Ruby-ju na defuse/password-hashing ili libsodium.
Šta je heš lozinke?
hash("hello") = 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
hash("hbllo") = 58756879c05c68dfac9866712fad6a93f8146f337a69afe7dd238f3364946366
hash("waltz") = c0e81794384491161f1777c232bc6bd9ec38f616560b120fda8e90f383853542Heš algoritam je jednostrana funkcija. Konvertuje bilo koju količinu podataka u fiksnu dužinu „otiska prsta“, i ovaj proces se ne može vratiti. Imaju osobinu da ako se ulaz malo promeni, generisani heš će biti potpuno drugačiji (pogledajte gornji primer). Ova osobina je vrlo pogodna za čuvanje lozinki. Zato što nam treba algoritam koji se ne može vratiti za enkripciju lozinki koje čuvamo, dok istovremeno osiguravamo da možemo verifikovati da li je lozinka koju korisnik unosi prilikom prijave tačna.
U sistemu naloga zasnovanom na heširanju, opšti tok registracije i autentifikacije korisnika je sledeći.
- Korisnik kreira svoj nalog.
- Lozinka se hešira i čuva u bazi podataka. Jednom kada se lozinka upiše na disk, nije dozvoljeno da bude u obliku plain teksta u bilo kom trenutku.
- Kada korisnik pokuša da se prijavi, sistem izvlači iz baze podataka već heširanu lozinku, i upoređuje sa heširanom lozinkom koju je korisnik uneo.
- Ako su heš vrednosti iste, korisniku se daje dozvola za pristup. Inače, korisniku se saopštava da su njegovi akreditivi za prijavu nevažeci.
- Svaki put kada neko pokuša da se prijavi, ponavljaju se koraci 3 i 4.
U koraku 4, nikada ne recite korisniku da li je pogrešio korisničko ime ili lozinku. Kao i generička poruka, uvek prikazujte: „Nevažeće korisničko ime ili lozinka.“ To je dovoljno. Ovo sprečava napadače da enumerišu važeća korisnička imena bez poznavanja lozinki.
Treba obratiti pažnju da je heš funkcija korišćena za zaštitu lozinke drugačija od heš funkcije naučene u kursu struktura podataka. Na primer, heš funkcija za implementaciju heš tabele je dizajnirana za brzu pretragu, ne za bezbednost. Samo kriptografska heš funkcija (cryptographic hash function) može se koristiti za heširanje lozinki. Kao SHA256, SHA512, RIPEMD i WHIRLPOOL su sve kriptografske heš funkcije.
Ljudima lako deluje da je to što Web developeri moraju da urade samo da izvrše kriptografsku heš funkciju da lozinke korisnika budu sigurne. Međutim, nije tako. Postoji mnogo načina za brzo povraćanje plain tekst lozinke iz jednostavnog heša. Postoji nekoliko tehnika lako implementirati koje značajno smanjuju efikasnost ovih „provaljivanja“. Na internetu postoje sajtovi specijalizovani za provaljivanje MD5-a, samo dostavite heš vrednost, manje od jedne sekunde možete dobiti rezultat provaljivanja. Očigledno, prosto heširanje lozinke je daleko od naših bezbednosnih zahteva. Sledeći odeljak će diskutovati neke uobičajene metode korišćene za provaljivanje jednostavnih heševa lozinki.
Kako provaliti heš?
Rečnički i brutalni napadi (Dictionary and Brute Force Attacks)

Najeasinji način provaljivanja heširanja je pokušaj da se pogodi lozinka, heš svaku pogodenu lozinku, i uporedi da li je heš pogodene lozinke jednak provaljenom hešu. Ako je jednak, pogodak je. Dva najčešća metoda napada guessovanja lozinke su rečnički napad i brutalni napad.
Rečnički napad koristi rečničke fajlove koji sadrže reči, fraze, uobičajene lozinke i druge stringove koji se mogu koristiti kao lozinke. Hešira svaku reč u fajlu, upoređuje ove heš vrednosti sa hešom lozinke koja se provaljuje. Ako su isti, ta reč je lozinka. Rečnički fajlovi se formiraju ekstrakcijom reči iz velikih blokova teksta, čak uključujujući neke stvarne lozinke iz baze podataka. Takođe može dalje obraditi rečnički fajl da bi ga učinio efikasnijim: na primer reč „hello“ prema internet slengu konvertovana u „h3110“.
Brutalni napad pokušava svaku moguću kombinaciju znakova za datu dužinu lozinke. Ovaj metod troši mnogo izračunavanja, takođe je najmanje efikasan metod provaljivanja heša, ali na kraju će pronaći tačnu lozinku. Zato lozinka treba da bude dovoljno dugačka, tako da obuhvatanje svih mogućih kombinacija znakova traje predugo da bi se podnelo, čime se napadač odustaje od provaljivanja.
Trenutno nema načina da se spreče rečnički ili brutalni napadi. Može se samo pokušati da ih učine neefikasnijim. Ako je dizajn sistema heširanja lozinke bezbedan, jedini metod provaljivanja heša je proći kroz rečnički ili brutalni napad svaki heš.
Metoda lookup tablica (Lookup Tables)

Za provaljivanje istog tipa heš vrednosti, lookup metoda je veoma efikasan način. Glavna ideja je unapred izračunati (pre-compute) heš svake lozinke u rečniku lozinki, zatim ih čuvati u tabeli zajedno sa odgovarajućom lozinkom. Dobar dizajn strukture lookup tablice, čak i ako sadrži milijarde heš vrednosti, i dalje može ostvariti stotine pretraga heša u sekundi.
Ako želite da osetite koliko je brza lookup metoda, pokušajte da koristite free hash cracker od CrackStation da provalite sledeći SHA256.
c11083b4b0a7743af748c85d343dfee9fbb8b2576c05f3a7f0d632b0926aadfc
08eac03b80adc33dc7d8fbe44b7c7b05d3a2c511166bdb43fcb710b03ba919e7
e4ba5cbd251c98e6cd1c23f126a3b81d8d8328abc95387229850952b3ef9f904
5206b8b8a996cf5320cb12ca91c7b790fba9f030408efe83ebb83548dc3007bdMetoda reverznih lookup tablica (Reverse Lookup Tables)

Ovaj napad omogućava napadaču da simultano pokrene rečnički ili brutalni napad na više heš vrednosti bez potrebe za unapred izračunatom lookup tablicom.
Prvo, napadač kreira tabelu korisničkih imena i odgovarajućih heševa lozinki iz baze podataka hakovanog korisničkog naloga, zatim napadač pogađa seriju heš vrednosti i koristi ovu lookup tabelu da pronađe korisnike koji koriste ovu lozinku. Obično mnogi korisnici koriste istu lozinku, zato je ovaj metod napada posebno efikasan.
Rainbow tabele (Rainbow Tables)
Rainbow tabele su tehnika zamene prostora vremenom. Slične lookup metodi, samo da bi tabelu učinili manjom, žrtvuju brzinu provaljivanja. Zato što su rainbow tabele manje, može se uskladiti više heš vrednosti po jedinici prostora, čime napad postaje efikasniji. Rainbow tabele koje mogu provaliti bilo koju MD5 vrednost od najviše 8 znakova su se već pojavile.
Sledeće, pogledajmo tehniku „dodavanje soli (salting)“, koja može učiniti da i lookup metoda i rainbow tabele postanu neefikasne.
Dodavanje soli (Adding Salt)

Lookup metoda i rainbow tabele su efikasne samo ako se sve lozinke heširaju na potpuno isti način. Ako dva korisnika imaju istu lozinku, imaći istu heš vrednost lozinke. Možemo izbeći ovaj napad „randomizacijom“ heša, tako da se kada ista lozinka hešira dva puta, dobijene heš vrednosti nisu iste.
Možemo kroz dodavanje random stringa u lozinku pre heširanja, ovaj dodati string se zove so (salt). Kao što je prikazato u gornjem primeru, ovo čini da ista lozinka bude enkriptovana u potpuno različite stringove svaki put. Potrebna nam je vrednost soli da verifikujemo da li je lozinka tačna. Obično se čuva zajedno sa heš vrednošću lozinke u bazi podataka naloga, ili kao deo heš stringa.
Vrednost soli ne mora da bude enkriptovana. Zato što randomizuje heš vrednost, lookup metoda, reverzna lookup metoda i rainbow tabele postaju neefikasne. Zato što napadači ne mogu unapred da znaju vrednost soli, zato nemaju načina da unapred izračunaju lookup tabelu ili rainbow tabelu. Ako se lozinka svakog korisnika hešira sa drugačijom soli, napad reverznom lookup metodom takođe neće biti efikasan.
Sledeće, pogledajmo neke uobičajene neispravne metode heširanja sa soli.
Pogrešni metodi: kratka so i ponovljena upotreba soli
Najčešća greška je korišćenje iste soli više puta pri heširanju, ili so je prekratka.
Ponovljena upotreba soli (Salt Reuse)
Česta greška je korišćenje iste soli svaki put pri heširanju, ova so je ili hard-codirana u program, ili se dobija random samo prvi put kada se koristi. Ovaj pristup je neefikasan, zato što ako dva korisnika imaju istu lozinku, i dalje će imati istu heš vrednost. Napadači i dalje mogu koristiti reverznu lookup metodu da vrše rečnički napad na svaki heš. Samo treba da primene fiksnu so na svaku pogodenu lozinku pre heširanja lozinke. Ako je so hard-codirana u popularnom softveru, lookup tabela i rainbow tabela mogu ugraditi ovu so, čime se olakšava provaljivanje generisanih heš vrednosti.
Kada korisnik kreira nalog ili menja lozinku, trebalo bi da koristi novu random so za enkripciju.
Kratak so (Short Slat)
Ako je so prekratka, napadači mogu unapred da naprave lookup tablice za sve moguće vrednosti soli. Na primer, ako so ima samo tri ASCII znaka, postoje samo 95x95x95=857,375 mogućnosti. Ovo deluje mnogo, ali ako svaka lookup tablica sadrži uobičajene lozinke od samo 1MB, 857,375 soli ukupno treba samo 837GB, jedan hard disk od 1TB ispod 100 dolara može rešiti problem.
Iz istog razloga, ne treba koristiti korisničko ime kao so. Za svaki servis, korisničko ime je jedinstveno, ali su predvidivi i često se ponavljaju na drugim servisima. Napadači mogu koristiti uobičajena korisnička imena kao so da uspostave lookup tablice i rainbow tablice za provaljivanje heš lozinke.
Da bi napadači ne mogu konstruisati lookup tabelu koja sadrži sve moguće vrednosti soli, so mora biti dovoljno dugačka. Dobar iskustvo je korišćenje soli iste dužine kao string izlaza heš funkcije. Na primer, izlaz SHA256 je 256 bitova (32 bajta), zato ova so takođe treba biti 32 random bajta.
Pogrešni metodi: dvostruko heširanje i čudne heš funkcije
Ovaj odeljak će predstaviti još jedno uobičajeno nerazumevanje heširanja lozinke: čudne kombinacije heš algoritma. Ljudima je lako da se zaborave, pokušavaju kombinovati različite heš funkcije za nadaju da će podaci postati sigurniji. Ali u praksi, ovo nema puno koristi. Ono donosi probleme interoperabilnosti između funkcija, čak može učiniti heš manje sigurnim. Nikada nemojte pokušati da kreirate sopstveni algoritam heširanja enkripcije, koristite standardne algoritme dizajnirane od strane eksperta. Neki će reći, korišćenje više heš funkcija će smanjiti brzinu izračunavanja, čime povećavati težinu provaljivanja. Ali postoje bolji načini da se proces heširanja uspori, o čemu ćemo kasnije govoriti.
Sledeći su neki primeri čudnih kombinacija heš funkcija viđenih na internetu.
- md5(sha1(password))
- md5(md5(salt) + md5(password))
- sha1(sha1(password))
- sha1(str_rot13(password + salt))
- md5(sha1(md5(md5(password) + sha1(password)) + md5(password)))
Nemojte koristiti nijednu od njih.
Napomena: Ovaj deo je kontroverzan. Primio sam nekoliko email-a koji smatraju da čudne heš funkcije imaju smisla, razlog je, ako napadač ne zna koju heš funkciju sistem koristi, napadaču je manje verovatno da će unapred izračunati rainbow tabelu ove čudne heš funkcije, zato će provaljivanje tražiti više vremena.
Kada napadač ne zna algoritam heširanja, ne može pokrenuti napad. Ali treba razmotriti Kirkhoffsov princip, napadači obično mogu dobiti izvorni kod (naročito za besplatni ili open source softver). Kroz pronalaženje odnosa lozinka-heš vrednost u sistem, liko se može inversno dedukovati algoritam enkripcije. Korišćenje iterativnog algoritma koji je teško paralelno izračunati (će biti diskutovano ispod), zatim dodavanje odgovarajuće soli sprečava rainbow tabelu napad.
Ako stvarno želite da koristite standardnu „čudnu“ heš funkciju, kao HMAC, takođe je u redu. Ali ako je vaša svrha smanjiti brzinu heš izračunavanja, onda možete pročitati sledeći deo o ekspanziji ključeva.
Ako kreirate novu heš funkciju, može doneti rizike, konstruisanje kombinacija heš funkcija će dovesti do problema interoperabilnosti funkcija. U poređenju sa prednostima koje donose, ovi su mali. Očigledno, najbolji način je korišćenje standardnih, potpuno testiranih algoritama.
"Heš kolizije ( Hash Collisions)")Heš kolizije (Hash Collisions)
Zato što heš funkcija konvertuje podatke bilo koje veličine u string fiksne dužine, mora postojati situacija da se različiti ulazi nakon heširanja dobiju isti string. Dizajn kriptografske heš funkcije (Cryptographic hash function) je da učini ove kolizije teško pronaljivim. Sada, kriptografi otkrivaju da je napad na heš funkciju sve lakši pronalaženje kolizije. Nedavni primer je MD5 algoritam, njegova kolizija je ostvarena.
Napad kolizije postoji string različit od korisničke lozinke, ali ima istu heš vrednost. Međutim, čak i za krhku heš funkcije kao MD5 pronalaženje kolizije zahteva veliku specijalizovanu snagu izračunavanja (dedicated computing power), zato je u praksi „slučajno“ pojavljivanje heš kolizije malo verovatno. Za praktičnu upotrebu, sigurnost MD5 sa soli i SHA256 sa soli je ista. Ipak, ako je moguće, treba koristiti sigurnije heš funkcije, kao SHA256, SHA512, RipeMD ili WHIRLPOOL.
Kako ispravno vršiti heš enkripciju
Ovaj odeljak predstavlja kako tačno treba heširati lozinku. Prvi deo predstavlja osnovno znanje, ovaj deo je obavezan. Kasnije opisuje kako na ovoj osnovi pojačati bezbednost, čime heširanje postaje teže provaljivim.
"Osnovno znanje: heširanje sa soli ( Hashing with Salt)")Osnovno znanje: heširanje sa soli (Hashing with Salt)
Već znamo da zlonamerni napadači koriste lookup tablice i rainbow tablice da brzo provaljuju obično heširanje. Takođe smo naučili da korišćenje random soli heširanje može rešiti ovaj problem. Ali, kakvu so treba koristiti i kako je mešati u lozinku?
So treba da generiše kriptografski sigurn generator pseudo-random brojeva (Cryptographically Secure Pseudo-Random Number Generator, CSPRNG). CSPRNG i uobičajeni generator pseudo-random brojeva su veoma različiti, kao funkcija rand() jezika „C“. Kao što ime kaže, CSPRNG je dizajniran za kriptografsku bezbednost, što znači da može obezbediti visoko random, potpuno nepredvidljive random brojeve. Ne želimo da so može biti predvidljiva, zato moramo koristiti CSPRNG. Sledeća tabela navodi neke CSPRNG metode trenutno glavnih programskih platformi.

Svaka lozinka svakog korisnika mora koristiti jedinstvenu so. Svaki put kada korisnik kreira nalog ili menja lozinku, trebalo bi koristiti novu random so. Nikada nemojte ponoviti upotrebiti određenu so. Ova so takođe treba da bude dovoljno dugačka, da bi imalo dovoljno soli za heširanje. Jednostavno pravilo je, so treba barem da bude iste dužine kao izlaz heš funkcije. Ova so treba da se čuva zajedno sa hešom lozinke u tabeli korisničkih naloga.
Koraci čuvanja lozinke:
- Koristite CSPRNG da generišete dovoljno dugu random so.
- Mešajte so u lozinku, i koristite standardnu heš funkciju lozinke za enkripciju, kao Argon2, bcrypt, scrypt ili PBKDF2.
- Čuvajte so i odgovarajuću heš vrednost zajedno u korisničkoj bazi podataka.
Koraci verifikacije lozinke:
- Izvucite so i odgovarajuću heš vrednost korisnika iz baze podataka.
- Mešajte so u lozinku koju je korisnik uneo, i koristite opštu heš funkciju za enkripciju.
- Uporedite rezultat prethodnog koraka, da li je isti sa heš vrednošću sačuvanom u bazi podataka. Ako su isti, lozinka je tačna; inače, lozinka je pogrešna.
U Web aplikaciji, uvek heširajte na strani servera
Ako pišete Web aplikaciju, možda se pitate gde tačno vršiti heširanje, da li u pregledaču korisnika koristiti JavaScript da heširate lozinku, ili da šaljete plain tekst na server pa tamo heširate?
Čak i ako ste u pregledaču već heširali pomoću JavaScript-a, i dalje moraćete na serveru da još jednom heširate dobijenu heš vrednost lozinke. Zamislite sajt, koji hešira lozinku koju korisnik unosi u pregledač, umesto da prenese na server pa tamo hešira. Da bi verifikovao korisnika, ovaj sajt prihvata heš iz pregledača, i upoređuje sa hešom u bazi podataka. Zato što lozinka korisnika nikada nije prenesena na server u plain tekst obliku, ovo deluje sigurnije, ali stvarnost nije takva.
Problem je, sa perspektive klijenta, heširana lozinka postaje lozinka korisnika. Sve što korisnik treba da uradi za autentifikaciju je da reče serveru heš vrednost lozinke. Ako napadač dobije heš vrednost korisnika, može je koristiti za prolaženje autentikacije, bez potrebe da zna plain tekst lozinku korisnika! Zato, ako napadač nekim sredstvom izvuče bazu podataka sajta, mogu slobodno koristiti naloge svih za direktan pristup, bez potrebe da pogadjaju bilo koju lozinku.
To ne znači da ne trebate heširati u pregledaču, ali ako to uradite, morate još jednom heširati na serveru. Heširanje u pregledaču je svakako dobra ideja, ali prilikom implementacije treba razmotriti sledeće tačke:
- Heširanje lozinke na klijentskoj strani nije zamena za HTTPS (SSL/TLS). Ako je veza između pregledača i servera nesigurna, napad „middle man“ može izmeniti JavaScript kôd, ukloniti funkciju enkripcije, čime dobija korisničku lozinku.
- Neki pregledači ne podržavaju JavaScript, a neki korisnici onemogućuju JavaScript funkcionalnost u pregledaču. Zato, za bolju kompatibilnost, vaša aplikacija treba da detektuje da li pregledač podržava JavaScript, ako ne, treba simulirati heširanje na klijentskoj strani na serveru.
- Heširanje na klijentskoj strani takođe zahteva dodavanje soli. Očigledno rešenje je da klijentska skripta zatraži so korisnika od servera. Ali se ne preporučuje ovako, zato što može omogućiti napadačima da detektuju da li je korisničko ime važeće bez poznavanja lozinke. Pošto ste već na serveru dodali so heširanoj lozinki (koristeći kvalitetnu so), na klijentskoj strani, korišćenje korisničkog imena (ili email-a) plus string specifičan za sajt (kao domen) kao so klijenta je takođe izvodljivo.
"Činiti lozinku težom za provaljivanje: spora heš funkcija ( Slow Hash Function)")Činiti lozinku težom za provaljivanje: spora heš funkcija (Slow Hash Function)
Dodavanje soli može osigurati da napadači ne mogu koristiti lookup tablice i rainbow tabele napade da provaljuju veliki broj heševa, ali i dalje ne može sprečiti da koriste rečničke ili brutalne napade. Visokokvalitetne grafičke kartice (GPU) i specijalizovani hardver mogu izvršiti milijarde heš izračunavanja u sekundi, zato su ovi napadi i dalje veoma efikasni. Da bismo smanjili efikasnost ovih napada, možemo koristiti tehniku zvanu ekspanzija ključeva (key stretching).
Namena je da se heš funkcija učini veoma sporom, čak i ako ima brz GPU ili specijalizovani hardver, rečnički i brutalni napadi će biti spori do neprijatnosti. Krajnji cilj je učiniti brzinu heš funkcije dovoljno sporom da napadač odustane, ali uzrokovano kašnjenje neće biti primetno za korisnike.
Implementacija ekspanzije ključeva koristi CPU-intenzivnu heš funkciju (CPU-intensive hash function). Nemojte pokušati da kreirate sopstvenu iterativnu heš funkciju. Ako je iteracija premalo, može je efikasno paralelno izračunati jak hardver, isto kao običan heš. Koristite standardne algoritme, kao PBKDF2 ili bcrypt. Ovde možete pronaći implementaciju PBKDF2 u PHP-u.
Ovi algoritmi uzimaju faktor bezbednosti ili broj iteracija kao parametar. Ova vrednost određuje koliko će heš funkcija biti spora. Za desktop softver ili aplikaciju pametnog telefona, najbolji način da odredite ovaj parametar je da pokrenete kratku performansnu benchmark test na uređaju, pronađite vrednost koja čini heš trajanje oko pola sekunde. Na ovaj način, program može garantirati bezbednost bez uticaja na korisničko iskustvo.
Ako želite da koristite ekspanziju ključeva u Web aplikaciji, treba da znate da potrebujete dodatne resurse za izračunavanje za obradu velikog broja zahteva za autentifikaciju, i ekspanzija ključeva lako može učiniti da server strad od DoS napada (DoS). Ipak, i dalje preporučujem korišćenje ekspanzije ključeva, samo postavite niži broj iteracija. Ovaj broj treba postaviti na osnovu kapaciteta izračunavanja vašeg servera i predviđenog broja zahteva za autentifikaciju po sekundi. Eliminisanje pretnji DoS može se postići zahtevom da korisnici pri svakoj prijave unose CAPTCHA. Sistem treba dizajnirati da se broj iteracija može lako podesiti.
Ako ste zabrinuti zbog tereta izračunavanja, ali želite da koristite ekspanziju ključeva u Web aplikaciji, možete razmotriti korišćenje JavaScript-a u pregledaču. JavaScript enkripciona biblioteka Univerziteta Stanford uključuje implementaciju PBKDF2. Broj iteracija treba postaviti dovoljno nisko, da bi se prilagodilo sporijim klijentima, kao mobilni uređaji. Istovremeno, ako pregledač korisnika ne podržava JavaScript, server treba preuzeti izračunavanje. Klijentska ekspanzija ključeva ne oslobađuje potrebu za heširanjem na serveru. Morate ponovo heširati heš vrednost generisanu na klijentu, isto kao pri obradi obične lozinke.
Heš enkripcija koja se ne može provaliti: heš ključeva i uređaji za heš lozinke
Dok god napadači mogu koristiti heš da provere da li je pogod lozinke tačna ili pogrešna, oni mogu vršiti rečnički ili brutalni napad. Sledeći korak je dodati tajni ključ (secret key) u heš enkripciju, tako da samo osoba koja zna ključ može verifikovati lozinku. Postoje dva načina implementacije, korišćenje AES algoritma za enkripciju heš vrednosti; ili korišćenje heš algoritma ključeva HMAC da uključi ključ u heš string.
Implementacija nije tako laka. Ovaj ključ u svim okolnostima, čak i ako sistem ima rupu i bio je hakiran, ne sme biti dostupan napadaču. Ako napadač potpuno uđe u sistem, gde god se ključ čuva, može ga naći. Zato, ključ mora biti sačuvan u eksternom sistemu, na primer specijalizovanom serveru za verifikaciju lozinke fizički izolisanom, ili povezan sa serverom, na primer specijalni hardverski uređaj, kao YubiHSM.
Snažno preporučujem sve velike servise (preko 100 hiljada korisnika) da koriste ovaj metod. Smatram da je za bilo koju servis sa preko 1 milion korisnika veoma neophodno.
Ako ne možete da priuštite više servera ili specijalizovani hardver, i dalje postoji način da koristite tehnologiju heš ključeva na standardnom Web serveru. Većina hakovanja baze podataka je zbog SQL injection napada, zato nemojte davati napadačima dozvolu za pristup lokalnom fajl sistemu (zabranite database servisu pristup lokalnom fajl sistemu, ako ima ovu funkcionalnost). Ako generišete random ključ i ga sačuvate u fajlu koji nije dostupan preko Web-a, zatim heširate sa solom, dobijena heš vrednost neće biti lako provaljena, čak i ako je baza podataka već pretrpela injection napad, i dalje je sigurna. Nemojte hard-cirati ključ u kôdu, treba ga random generisati pri instalaciji aplikacije. Ovo nije tako sigurno kao korišćenje nezavisnog sistema, jer ako Web aplikacija ima SQL injection tačku, možda postoje drugi problemi, kao Local File Inclusion ranjivost (Local File Inclusion), napadači mogu iskoristiti da pročitaju lokalni fajl ključa. Svaku kako, ova mera je bolja od ničega.
Obratite pažnju, heš ključ ne znači da se ne treba dodavati so. Napredni napadači će na kraju naći način da pronađu ključ, zato je još veoma važno da heš lozinke i dalje bude sojen i proširen ključevima.
Ostale bezbednosne mere
Heš lozinke samo štiti lozinku kada je bezbednost narušena. Ne čini ceo aplikacija sigurnijom. Prvo mora mnogo toga biti urađeno da bi se osiguralo da heš vrednosti lozinke (i drugi korisnički podaci) ne budu ukradeni.
Čak i iskusni programeri moraju da uče bezbednosno znanje da bi napisali sigurnu aplikaciju. Ovde postoje važni resursi o ranjivostima Web aplikacija: The Open Web Application Security Project (OWASP). Postoji takođe dobar uvod: OWASP Top Ten Vulnerability List. Osim ako ne razumete sve ranjivosti na listi, nemojte pokušavati da napišete Web aplikaciju koja obrađuje osetljive podatke. Poslodavci takođe imaju odgovornost da osiguraju da svi programeri imaju dovoljno obuke o razvoju sigurnih aplikacija.
Treća strana „penetration testing“ vaše aplikacije je vrlo dobra ideja. Čak i najbolji programeri mogu praviti greške, zato je smisno da bezbednosni eksperti audtiraju kôd tražeći potencijalne ranjivosti. Pronađite pouzdanu instituciju (ili regruter) za redovnu auditu kôda. Bezbednosna audita treba započeti u ranom razvoju i trajati kroz ceo proces razvoja.
Praćenje vašeg sajta da otkrije napade je takođe veoma važno. Preporučujem da zaposlite bar jednog punog zaposlenog odgovornog za monitoring i rešavanje bezbednosnih ranjivosti. Ako ranjivost nije otkrivena, napadači mogu kroz sajt koristiti malware da inficiraju posetioci, zato otkrivanje i pravovremeno rešavanje ranjivosti je veoma važno.
Česta pitanja
Koji heš algoritam treba da koristim?
Može koristiti:
- Pažljivo dizajnirani algoritmi ekspanzije ključeva kao PBKDF2, bcrypt i scrypt.
- Portable PHP password hashing framework od OpenWall.
- Implementacije PBKDF2 u PHP-u, C#, Javi i Ruby-ju.
- Sigurna verzija crypt.
Ne može koristiti:
- Brze kriptografske heš funkcije, kao MD5, SHA1, SHA256, SHA512, RipeMD, WHIRLPOOL, SHA3 itd.
- Nesigurna verzija crypt().
- Bilo koji sopstveni dizajnirani algoritam enkripcije. Trebalo bi samo koristiti tehnologije koje su u javnoj domeni, potpuno testirane od strane iskusnih kriptografa.
Iako još uvek ne postoji veoma efikasan metod napada na MD5 ili SHA1, previše su stari da bi se široko smatrali adekvatnim za čuvanje lozinki (možda neumestno). Zato ih ne preporučujem. Ali postoji izuzetak, PBKDF2 često koristi SHA1 kao osnovnu heš funkciju.
Kako resetovati lozinku kada korisnik zaboravi lozinku?
Ovo je moje lično mišljenje: svi trenutno široko korišćeni mehanizmi resetovanja lozinke su nesigurni. Ako imate visoke zahteve za bezbednost, kao servis enkripcije, nemojte dozvoliti korisnicima da resetuju lozinku.
Većina sajtova šalje email korisnicima koji su zaboravili lozinku za autentifikaciju. Da biste ovo postigli, treba random generisati token za jednu upotrebu, direktno povezan sa korisničkim nalogom. Zatim ovaj token meša u link za resetovanje lozinke i šalje na email korisnika. Kada korisnik klikne na link za resetovanje lozinke koji sadrži važeći token, traži im da unesu novu lozinku. Osigurajte da token važi samo za jedan nalog, kako bi sprečilo napadače da nakon dobijanja tokena iz inbox-a koriste da resetuju lozinke drugih korisnika.
Token mora biti korišćen u roku od 15 minuta, i odmah se stavlja van snage nakon korišćenja. Kada se korisnik uspešno prijavi (pokaže da se još uvek seća svoje lozinke), ili ponovo zatraži token, dobra je praksa da stari token postane nevažeći. Ako token nikada ne ističe, može se stalno koristiti za hakovanje korisničkog naloga. Email (SMTP) je plain text protokol, na internetu postoje mnogi zlonamerni ruteri koji presreću email poruke. Dok korisnik menja lozinku, emailovi koji sadrže linkove za resetovanje lozinke dugo vremena nedostaju zaštitu, zato treba što pre istaći token, kako bi se smanjio rizik da korisničke informacije budu izložene napadačima.
Napadači mogu falsifikovati token, zato nemojte čuvati informacije o nalogu i vreme isteka u njemu. Trebalo bi da postoje u ne-mogućem binarnom obliku i služe samo za identifikaciju određenog zapisa korisnika u bazi podataka.
Nikada nemojte slati novu lozinku korisniku putem email-a. Setite se da pri resetovanju lozinke generišete novu so za enkripciju, nemojte ponovo koristiti staru so koja se već koristila za heš lozinke.
Šta treba raditi ako je baza podataka naloga procurila ili hakirana?
Vaš prvi zadatak je da odredite do kog stepena je sistem izložen, zatim popravite rupu koju je napadač iskoristio. Ako nemate iskustvo u rešavanju upada, snažno preporučujem da unajmite treću stranu bezbednosnu kompaniju da uradi ovo.
Sakrivši rupu i nadajući se da niko ne zna, da li je štedište i zanosno? Ali ovo samo otežava vašu situaciju, zato što bez znanja korisnika izlažete njihove lozinke i lične informacije riziku od izlaganja. Čak i ako se još uvek nije desilo ništa, trebalo bi što pre obavestiti korisnike. Na primer, postaviti link na početnu stranu, koji vodi do detaljnijeg objašnjenja ovog problema; ako je moguće, poslati email svakom korisniku da ga obavesti o trenutnoj situaciji.
Obrazložite korisnicima kako tačno štite njihove lozinke: najbolje je korišćenje heširanja sa soli. Ali, čak i uz heširanje sa soli, zlonamerni hakeri i dalje mogu koristiti rečničke i brutalne napade. Ako korisnici koriste istu lozinku na mnogim servisima, zlonamerni hakeri će koristiti pronađene lozinke da pokušaju da se prijave na druge sajtove. Obavestite korisnike ovom riziku, predložite im da promene sve slične lozinke, bez obzira na kom servisu se koriste. Obavezite ih da sledeći put kada se prijave na vaš sajt promene lozinku. Većina korisnika će pokušati „izmeniti“ svoju lozinku u originalnu lozinku radi lakšeg pamćenja. Trebalo bi koristiti trenutnu heš vrednost lozinke da osigurate da korisnici ne mogu to da urade.
Čak i uz zaštitu heširanja sa soli, postoji mogućnost da napadači brzo provalje neke slabše lozinke. Da biste smanjili priliku da napadači koriste ove lozinke, trebalo bi poslati autentikacijski email na naloge ovih lozinki, sve dok korisnik ne promeni lozinku. Može se referisati na prethodno pitanje: kako resetovati lozinku kada korisnik zaboravi lozinku? U njemu postoje neki ključni tačke implementacije autentikacije putem email-a.
Takođe recite vašim korisnicima koje lične informacije sajt čuva. Ako vaša baza podataka uključuje brojeve kreditnih kartica, trebalo bi obavestiti korisnike da pažljivo provere nedavne račune i ponište ovu kreditnu karticu.
Koju politiku lozinki treba koristiti? Da li treba koristiti jake lozinke?
Ako vaš servis nema stroge bezbednosne zahteve, nemojte ograničavati korisnike. Preporučujem da kada korisnik unosi lozinku, stranica prikazuje jačinu lozinke, i njima ostavlja da odluče koliko sigurnu lozinku žele. Ako imate specifične bezbednosne potrebe, onda treba implementirati lozinku dužine barem 12 znakova, i zahtevati barem dva slova, dva broja i dva simbola.
Nemojte previše često forsirati vaše korisnike da menjaju lozinku, najviše jednom svakih 6 meseci, previše puta, korisnici će osetiti umor. Suprotno, bolje je edukovati korisnike, da kada osete da je lozinka možda procurila, aktivno je promene, i podsetiti korisnike da ne daju svoju lozinku bilo kome. Ako je poslovno okruženje, podstaknite zaposlene da koriste radno vreme da upamte i koriste svoje lozinke.
Ako napadač hakira bazu podataka, ne može li direktno zameniti heš vrednost i prijaviti se na bilo koji nalog?
Da, ali ako neko hakira vašu bazu podataka, verovatno već može pristupiti svemu na vašem serveru, ne moraju se prijaviti na vaš nalog, mogu dobiti šta žele. Svrha heš lozinke (za sajt) nije zaštititi hakirani sajt, već zaštititi lozinke u bazi podataka kada je hakiranje već desilo.
Možete sprečiti da se heš lozinke promene pri injection napadu postavljanjem dva dozvole za konekciju baze podataka, jedna za kreiranje korisnika, jedna za prijavu korisnika. Kôd „kreiranje korisnika“ treba moći da čita i piše u tabelu korisnika; ali kôd „prijava korisnika“ treba samo moći da čita tabelu korisnika, ne može da piše.
Zašto koristiti specijalan algoritam kao HMAC, umesto samo mešati ključ u lozinku?
Kao MD5, SHA1, SHA2 i Hash funkcije koriste Merkle–Damg?rd, što čini da su lako podložne takozvanom napadu produženja dužine (length extension attack). To znači datoj heš vrednosti H(X), za bilo koji string Y, napadač može izračunati vrednost H(pad(X)+Y) bez poznavanja vrednosti X. Gde je pad(X) funkcija popunjavanja heš funkcije.
To znači, napadač ne znajući ključ, i dalje može na osnovu date heš vrednosti H(key+message) izračunati H(pad(key+message)+extension). Ako se ova heš vrednost koristi za autentifikaciju i oslanja se na ključ unutar nje da spreči napadača da falsifikuje poruku, ovaj metod više ne funkcioniše. Zato što napadač može konstruisati važeću heš vrednost koja sadrži message+extension bez poznavanja ključa.
Trenutno nije jasno kako napadači mogu iskoristiti ovaj napad da brzo provalje heš lozinki. Ipak, zbog pojavljivanja ovog napada, ne preporučuje se korišćenje običnih heš funkcija za heširanje ključeva. U budućnosti možda neki napredni kriptograf jednog dana otkrije novu ideju za korišćenje napada produženja dužine, čime će brže provaliti lozinku, zato je bolje koristiti HMAC.
Da li so treba dodati pre ili posle lozinke?
Svejedno, izaberite jednu i održavajte dosledan stil, kako bi se izbegli problemi interoperabilnosti.
Zašto heš kôd u ovom članku uvek upoređuje heš vrednosti fisknim vremenom?
Korišćenje fiksnog vremena za upoređivanje heš vrednosti može sprečiti napadače da koriste napad zasnovan na vremenskoj razlici u online sistemu, čime dobijaju heš vrednost lozinke, zatim lokalno provaljuju.
Standardni metod za upoređivanje da li su dva niza bajtova (string) ista je, počevši od prvog bajta, upoređuje svaki bajt redom. Čim se utvrdi da je neki bajt različit, može se znati da su različiti, odmah vraća false. Ako se prođe kroz ceo string bez pronalaska različitog bajta, može se potvrditi da su dva stringa ista, može se vratiti true. To znači da vreme potrebno za upoređivanje dva stringa nije isto ako su dužina ista i različita. Što je duže podudaranje na početku, to duže traje.
Na primer, standardno upoređivanje stringova „XYZABC“ i „abcxyz“ će odmah videti, da je prvi karakter različit, ne treba proveravati ostatak stringa. Suprotno, kada se upoređuju „aaaaaaaaaaB“ i „aaaaaaaaaaZ“, algoritam upoređivanja treba proći kroz sva „a“ pre poslednjeg karaktera, zatim može znati da su različiti.
Pretpostavimo da napadač pokuša da hakira online sistem, ovaj sistem ograničava da se samo jednom može pokušati korisnička autentifikacija u sekundi. Takođe pretpostavimo da napadač već zna sve parametre heš lozinke (so, tip heš funkcije itd.), osim heš vrednosti i same lozinke. Ako napadač može tačno da meri koliko dugo online sistem treba da uporedi pogodenu lozinku i pravu lozinku, on može koristiti timing napad da dobije heš vrednost lozinke, zatim lokalno provaljuje, čime zaobilazi ograničenje frekvencije autentikacije sistema.
Prvo napadač priprema 256 stringova, čiji prvi bajt heš vrednosti sadrži sve mogućnosti. Šalje svaki string sistemu da pokuša prijavu, i beleži vreme odziva sistema. Najduže trajani string je onaj gde se prvi bajt podudara. Nakon što napadača sazna prvi bajt, može na isti način nastaviti da pogodi drugi bajt, treći bajt itd. Jednom kada napadač dobije dovoljno dugačke delove heš vrednosti, može na svom mašini da provali, bez ograničenja online sistema.
Izvršavanje ovakvog napada na internetu deluje nemoguće. Međutim, neko je već implementirao, i dokazano je da je praktično. Zato kôd pomenut u ovom članku koristi fiksno vreme za upoređivanje stringova, bez obzira na veličinu stringa.
"Kako funkcija „sporo poređenje ( slowequals)“ radi?")Kako funkcija „sporo poređenje (slowequals)“ radi?
Prethodno pitanje objašnjava zašto je „sporo poređenje“ neophodno, sada objasnjavamo kako kôd radi.
private static boolean slowEquals(byte[] a, byte[] b)
{
int diff = a.length ^ b.length;
for(int i = 0; i < a.length && i < b.length; i++)
diff |= a[i] ^ b[i];
return diff == 0;
}Ovaj kôd koristi XOR operator „^“ za upoređivanje da li su dva cela broja jednaka, umesto operatora „==". Sledeće objašnjenje. Kada i samo kada su dva bita jednaka, rezultat XOR-a će biti nula. Zato što:
0 XOR 0 = 0, 1 XOR 1 = 0, 0 XOR 1 = 1, 1 XOR 0 = 1
Ako ovo primenimo na svaki bit u celom broju, kada i samo kada svi bitovi dva cela broja jednaki, rezultat je 0.
Zato, u prvom redu kôda, ako je a.length jednako b.length, dobija se 0, inače se dobija nenulta vrednost. Zatim se koristi XOR za poređenje svakog bajta u nizu, i rezultat se OR-ira sa diff. Ako bilo koji bajt nije isti, diff će postati nenulta vrednost. Zato što OR operacija nema funkciju „postavi na 0", nakon završetka petlje diff je 0 samo u jednom slučaju, to jeste da su pre petlje dužine dva niza bila jednaka (a.length == b.length), i svaki bajt u nizu je isti (svaki XOR rezultat je nije 0).
Treba koristiti XOR, umesto operatora „" za poređenje celih brojeva, zato što „" se obično kompilira u iskaz grananja. Na primer, u C kôdu „diff &= a == b“ može se kompilirati u sledeća x86 asemblera:
MOV EAX, [A]
CMP [B], EAX
JZ equal
JMP done
equal:
AND [VALID], 1
done:
AND [VALID], 0Grananje u njemu čini da vreme izvršenja kôda nije fiksno, zavisi od stepena jednakosti dva celobrojne promenljive i mehanizma predikcije skoka unutar CPU-a.
Dok se C kôd „diff |= a ^ b“ kompilira kao sledeće, njegovo vreme izvršenja nije vezano za to da li su dve promenljive jednake:
MOV EAX,[A]
XOR EAX,[B]
OR [DIFF],EAXZašto treba heširati?
Korisnik unosi lozinku na vašem sajtu, zato što veruju da možete garantovati bezbednost lozinke. Ako vaša baza podataka bude hakirana od strane hakera, a korisničke lozinke nisu zaštićene, zlonamerni hakeri mogu koristiti ove lozinke da pokušaju da se prijave na druge sajtove i servise (većina korisnika koristi istu lozinku svuda). Ovo se tiče ne samo bezbednosti vašeg sajta, već i bezbednosti korisnika. Vi imate odgovornost da budete odgovorni za bezbednost korisnika.
#bezbednost #bezbednost lozinke #bezbednost informacija
Strategija front-end u odbrani crawlera
Kamen temeljac algoritma javnog ključa: veliki prost brojevi
Referenca: https://coolcao.com/2018/07/24/Salted-Password-Hashing/, organizovao: Marko Marković
