21. diel - Bezpečnostné hrozby - Ako správne ukladať heslá?
V minulej lekcii, Blog v Spring Boot - Validácia registračného formulára, sme pridali DTO pre registračný formulár a implementovali sme jeho validáciu.
V našom kurze sa dostávame k ukladaniu užívateľských hesiel, čo je potenciálna bezpečnostná hrozba. V dnešnej dobe GDPR zákonov a vysokého bezpečnostného štandardu, sa chceme vo vlastnom záujme vyhnúť bezpečnostným chybám v našich aplikáciách. Záleží nám na tom, aby ste boli naozaj dobrí. Preto naše kurzy obsahujú aj vybrané témy z kurzov Dobrých praktík a Kyberbezpečnosti. Ako teda heslá bezpečne uložiť?
Heslá sa do databázy neukladajú
Pre ukladanie hesiel nielen do databázy platí jedno jednoduché pravidlo:
Žiadne heslá do databázy nikdy neukladáme !
K databáze sa môžu dostať nepovolané osoby, napr. pri hackerskom útoku na webhosting alebo našu aplikáciu. Musíme si uvedomiť, že heslá, najmä ak sú priradené k emailovým adresám, sú veľmi citlivé dáta. Veľké percento užívateľov totiž používa to isté heslo aj k svojej emailovej schránke a na ďalšie služby. Útočník by teda dostal tisíce vstupeniek do emailových schránok našich užívateľov. Cez email nie je prakticky už žiadny problém dostať sa kamkoľvek inam, stačí si na ktorúkoľvek službu, napr. Facebook, nechať poslať nové heslo do emailovej schránky, ktorú útočník ovláda. Aj keby u užívateľov email uložený nebol, je možné užívateľa obvykle jednoducho spojiť podľa mena alebo jeho príspevkov s nejakým iným účtom na internete, získať jeho emailovú adresu, následne prístup do jeho schránky a tak aj k ďalším ním využívaným službám. Toto je okrem iného dôvod, prečo emailové schránky obvykle chcú dvojfázové overenie, avšak nie všetci ho budú mať aktívne.
Na overenie, že užívateľ zadal správne heslo, jeho heslo ale predsa mať uložené potrebujeme. Alebo nie?
Hashovanie hesiel
Hashovanie [hešovanie] je jednosmerný prevod nejakých
vstupných dát na inú hodnotu. Ak zahashujeme užívateľské heslo, získame
jeho odtlačok. Ide obvykle o reťazec zdanlivo nesúvisiacich
znakov a číslic a nemožno z neho pôvodné heslo zistiť.
Napr. pre heslo ictDemy1
by sme dostali nasledujúci reťazec:
$2a$10$bPEtRTvJ1A1Cu4mJwIbJM./mGB8myOGaRIcw07IQN0c2Ih32k.Ht2
Namiesto hesla užívateľa ukladáme vždy len jeho odtlačok, z ktorého nemožno pôvodné heslo získať.
Hashovacie algoritmy sú kryptograficky navrhnuté tak, že ich nemožno prelomiť inak ako skúšaním všetkých kombinácií. To pri dobre zvolenom algoritme a soli (viď ďalej) je možné v rozumnom čase vykonať len v minimálnej miere. Okrem ukladania hesiel sa hashovanie využíva tiež napr. pre elektronické podpisy alebo kryptomeny.
Overenie hesla pomocou odtlačkov
Ako teraz ale zistíme, že používateľ zadal správne heslo? To je ľahké. Z hesla, ktoré užívateľ zadal, spočítame odtlačok. Ten porovnáme s odtlačkom, ktorý máme uložený u daného užívateľa v databáze. Pokiaľ sú odtlačky rovnaké, musel zadať aj rovnaké heslo!
Takúto autentizáciu si môžeme predstaviť, ako by si užívateľ ľahol do piesku a my si odfotili jamku, ktorá po ňom zostala. Keď príde znova, necháme ho znova ľahnúť si do piesku a porovnáme, či jamku po ňom vyzerá rovnako, ako jamku, čo máme pri jeho účte. Ak áno, ide o toho istého človeka (ak výrazne nepribral, čo sa pri hesle nestane ). Keď nám niekto ukradne knihu s fotografiami jamiek, nikdy z nej nezistí, ako vyzerajú tváre ľudí, ktorí k nám chodia. Rovnako tak, keď nám niekto ukradne databázu webu s hashy hesiel, nikdy nezistí heslá našich užívateľov.
Nasledujúci dva ľudia majú v piesku rovnaké odtlačky a jedná sa tak o rovnakého človeka:
A tu sa niekto snaží vydávať za niekoho iného, odtlačky nesúhlasia:
Hashovanie vs. šifrovanie
Hashovanie si nebudeme pliesť so šifrovaním.
Šifrovanie je obojsmerným prevodom danej hodnoty a bolo by potom možné heslo spätne získať. Heslá šifrujú napr. kľúčenky, ktoré potrebujú dostať pôvodné heslo, aby ho mohli odoslať do nejakej ďalšej služby. My heslo nikam neposielame, iba overujeme, že používateľ zadal to isté. Preto ho nešifrujeme, ale hashujeme. Šifru je možné dešifrovať, hash nie.
Ďalšou častou chybou je, že si ľudia myslia, že hasha sú unikátne. Pravdepodobnosť je síce zanedbateľná, ale pre dve rôzne heslá môže hashovacia funkcia vygenerovať rovnaký hash. Pre overenie hesla to nijako nevadí, ale hasha by sme nikdy nemali používať ako unikátne identifikátory.
Slabiny hashovania
Takmer na každý známy web bol už vykonaný hackerský útok, veľmi často aj úspešný. Ako už bolo povedané, hashovacie algoritmy sú jednocestné. To môžeme chápať tak, že časť informácií z pôvodného hesla jednoducho zahadzujeme. Hackeri sú však vynaliezaví a rovnako prišli na v zásade dve metódy, ako pôvodné heslo z odtlačku predsa len získať. Stručne si ich tu spomenieme, aby sme vedeli, na čo si dávať pozor.
Slabina 1 - Algoritmus
Hashovanie je navrhnuté tak, aby výpočet odtlačku počítača chvíľu trval. Počítače sa však stále zrýchľujú. Pokiaľ budeme používať zastarané algoritmy ako napr. md5, môžu hackeri jednoducho vyskúšať všetky možné heslá kombináciami znakov do určitej dĺžky. Z týchto hesiel potom budú počítať odtlačky, kým sa nestrefia do toho, ktorý máme uložený v databáze. U moderných algoritmov nemožno na súčasných počítačoch hasha takto rýchlo počítať.
Spring Boot Security má v sebe zabudované dva algoritmy späté s hashovaním hesiel:
- bcrypt – Moderný algoritmus za vývojárov rieši
pridávanie soli k heslu a prípadnú voľbu hashovacieho algoritmu, preto aj
tento algoritmus využijeme. Implementáciu algoritmu možno nájsť v triede
BCryptPasswordEncoder
. - Argon2 – Ešte modernejšia alternatíva k bcryptu.
Implementáciu algoritmu možno nájsť v triede
Argon2PasswordEncoder
.
U algoritmu možno obvykle nastaviť aj náročnosť výpočtu
(číslo 10
v ukážke hesha na začiatku lekcie). Čím väčšia
bude, tým dlhšie bude trvať odtlačok vypočítať. Nie je to však
ekvivalent bezpečnosti, príliš vysoká hodnota môže spôsobovať
výkonnostné problémy aplikácie.
Slabina 2 - Neosolené heslo
Určite viete, že veľa ľudí používa heslá ako 123456 a podobne. Aj rozumnejšie zvolené heslá by sa nám však v databáze opakovali. S touto problematikou súvisí napr. birthday paradox - Aká myslíte, že je pravdepodobnosť, že majú v skupine 23 náhodne zvolených ľudí dvaja ľudia narodeniny v rovnaký deň? Je to 50 %.
Nie je teda problém podľa početnosti výskytu rovnakých odtlačkov v databáze hesla hádať. Alebo mať na najpoužívanejšie heslá predpočítané odtlačky na najznámejšie algoritmy. Takým databázam hashov sa hovorí rainbow tables. Možno sa jednoducho pozrieť, či niekto náhodou nemá odtlačok "73cd1b16c4fb83061ad18a0b29b9643a68d4640075a466dc9e51682f84a847f5" a hneď vieme, že jeho heslo je "superman".
Z tohto dôvodu k heslu užívateľa pred výpočtom hasha ešte vždy niečo pripojíme, napr. ID užívateľa, čím používatelia aj s najhlúpejšími heslami budú mať hasha, ktoré nebudú v dúhových tabuľkách. Hovoríme, že tým heslo prisolíme.
Soľ je obvykle aj u každého užívateľa iný reťazec. Pokiaľ si teda 10 našich užívateľov nastaví heslo "qwerty", s použitím soli všetci budú mať iný hash, pretože sa ku "querty" pridá nejaká ďalšia hodnota. Vytrvalý hacker, čo si dá tú prácu predpočítať pár častých hesiel pre jednu našu soľ, tak nemôže výsledné hashe použiť pre ďalších užívateľov a pri každom musí začínať znova.
Moderné algoritmy si soľ generujú samy a ukladajú si ju priamo do výsledku (časť výslednej hodnoty hashovacej funkcie je odtlačok a časť soľ). Nemusíme sa teda o nič starať. Je však vhodné vždy overiť, že to zvolený algoritmus robí alebo sa očakáva, že soľ pripojíme my.
V budúcej lekcii, Blog v Spring Boot - UserEntity a UserRepository, vytvoríme entitu pre užívateľov a repositár so základnými CRUD operáciami.