Blog - Kodrla.eu

Jak ve webových aplikacích ukládat hesla

Píšeme-li webovou aplikaci (nebo zodpovídáme za její bezpečnost), dříve nebo později narazíme na potřebu ověřit identitu uživatele a k tomu budeme chtít jeho heslo. Pojďme si říct, jak hesla ve webové aplikaci ukládat bezpečně.

Proces ověření identity uživatele ve webové aplikaci může vypadat nějak takto:

  1. Uživatel požádá o přístup k nějakému prostředku (webové stránce), kam anonymní uživatel nemá přístup. Aplikace buď zareaguje chybu autorizace (např. 401 – Unauthorized), nebo uživatele přesměruje na přihlašovací stránku. To bude náš případ.
  2. Na přihlašovací stránce uživatel vyplní formulář, kam do příslušných kolonek zadá uživatelské jméno (identifikace uživatele) a heslo (pro jeho autentizaci).
  3. Systém podle uživatelského jména najde příslušný záznam v databázi, obsahující data o uživateli.
  4. Systém ověří shodu hesla zadaného s heslem uživatele.
  5. Podle výsledku ověření shody je následně uživatel autentizovaný, nebo není.

Poznámka: Pokud si nejste jistí výrazy jako identifikace, autentizace a autorizace, podívejte se na článek AAA, aneb Ťuk, ťuk! Kdo tam? věnovaný právě těmto pojmům.

Co odnesl čas?

V hluboké minulosti nebylo neobvyklé, že se hesla do databáze ukládala v prostém textu (plaintext). Kdokoli, kdo měl přístup do příslušné tabulky, si mohl hesla zobrazit. Takže se ukázalo, že to není úplně ideální, a začal se hledat nový způsob ukládání. A co udělám, když nechci, aby kdokoli mohl data číst? Správně, zašifruju je. Teď už si hesla nemohl přečíst každý, kdo k nim získal přístup.

Přesto ani šifrování nebylo v mnoha scénářích optimální. Aby mohla aplikace ověřit shodu hesla zadaného do přihlašovacího formuláře a hesla uloženého v databázi, musí mít někde uložený šifrovací klíč. Vždycky tak existovalo riziko, že se k šifrovacímu klíči, kterým jsou hesla zašifrovaná, dostane nepovolaná osoba. Stačí mít přístup k serveru, k aplikaci, konfiguračním souborům nebo zdrojovým kódům. Když databáze unikla, útočníci mohli zkusit hesla dešifrovat.

A co se osvědčilo?

Jako dosud nejlepší varianta se nakonec ukázalo ukládání hashů hesel. Má to nepopiratelnou výhodu: hashování je jednosměrné, z hashe nelze zpětně získat heslo (resp. vstup obecně). Takže super, jdeme ukládat do databáze místo hesla jen jeho hash! Když se uživatel přihlásí, z hesla, které zadal do přihlašovacího formuláře, spočítáme hash a ten porovnáme s hashem v databázi. Souhlasí? Prima, pustíme ho dál. Nesouhlasí? Lituji, zkus to znovu.

Problém hashe je, že dva stejné vstupy mají i stejný výstup. Když dva uživatelé použijí stejné heslo, v databázi se objeví stejný hash. V případě úniku databáze útočník vidí, kdo všechno má stejné heslo. Nezná ho, ale může začít vymýšlet další postup. Může vyzkoušet hashe běžných hesel. Může zkusit heslo vylákat od některého jiného uživatele - phishing, social engineering, hesla stejného uživatele (podle e-mailu) z jiných uniklých databází. A jakmile získá heslo jednoho uživatele, podle hashe ví, kdo další má stejné heslo.

Aby se zamezilo možnosti zjišťovat shodná hesla, začalo se k heslu přidávat náhodný řetězec, tzv. salt, kryptografická sůl. Salt je náhodný unikátní řetězec, který – přidáním k heslu – způsobí, že dvě stejná hesla už nejsou stejná, tím pádem už nemají ani stejný hash.

Alice i Bob mají oba stejné heslo „tajnéheslo“. Aplikace k němu ale přidává salt. Pro Alici to je 6qi8pG, pro Boba yWocnD. Vstupem pro hashovací funkci se tak u Alice stane řetězec „6qi8pGtajnéheslo“, u Boba „yWocnDtajnéheslo“. Oba řetězce budou mít jiný hash. Ačkoli si Alice a Bob pamatují jen své heslo, pro hashování a porovnání s hashem v databázi se použijí odlišné řetězce.

Poznámka: Jestli aplikace připojí salt na začátek, konec nebo nějak zkombinuje, to je závislé na konkrétní implementaci a nemá na ověření vliv. Jen je nutný konzistentní postup jak při vytváření hesla, tak při jeho ověřování. A to se týká i použitého hashovacího algoritmu – jiný algoritmus nebo jiné připojení salt způsobí neúspěch ověření hesla.

Protože uživatelé udržují v tajnosti své heslo, není nutné salt v databázi šifrovat, můžeme ji ukládat v prostém textu; jejím účelem není zajistit důvěrnost hesla, ale aby každý hash byl unikátní. Tajná zůstává pouze část pocházející od uživatele, tedy samotné heslo.

A jak je to v praxi?

Co ale je důležité, salt si musíme uložit do databáze a být schopni spárovat uživatele a jeho salt, protože bez znalosti salt nebude možné při přihlášení shodu hesla ověřit. Obvykle se to řeší uložením do stejného záznamu, jako ostatní data uživatele (username, e-mail…). Dále musí být salt dostatečně dlouhá, aby se snižovala efektivita útoků předpočítanými tabulkami (tzv. rainbow tables). OWASP doporučuje minimálně 32 znaků, což odpovídá 16 bajtům kryptograficky náhodných binárních dat. Ovšem, díky tomu, že uživatel salt znát nepotřebuje, ta je uložena jen v databázi a předává se jen aplikaci, nepředstavuje to žádný problém.

Přihlášení uživatele s pomocí salt pak probíhá následovně:

  1. Na přihlašovací stránce uživatel vyplní formulář, kam do příslušných kolonek zadá uživatelské jméno (identifikace uživatele) a heslo (pro jeho autentizaci).
  2. Systém podle uživatelského jména najde příslušný záznam v databázi, obsahující data o uživateli.
  3. Systém z nalezeného záznamu vezme salt uživatele a připojí ji k heslu, které uživatel zadal do formuláře.
  4.  Ze vzniklého řetězce spočítá hash a ten porovná s hashem uloženým v databázi.

Postupem času se ukázalo, že i salt nezaručuje úplnou odolnost vůči útokům na hashe. Moderní grafické karty dokážou u rychlých hashovacích funkcí počítat miliardy hashů za sekundu. Proto se začaly objevovat „pomalé“ hashovací funkce, jako Argon2, bcrypt nebo scrypt. Ty jsou navrženy tak, aby například pomocí iterací záměrně zpomalovaly výpočet hashe, který trvá řádově ve stovkách milisekund. Navíc mají i vyšší nároky na paměť, takže snaha o hromadné ověření hesel nejen trvá déle, ale vyžaduje větší výpočetní výkon. Uživatel si takové „sabotáže“ vůbec nevšimne, ale útočníkovi dramaticky prodlužuje čas potřebný k prolomení hesel.

Díky tomu je útok hrubou silou výrazně dražší nejen na čas, ale i na hardware. Výkonné GPU nebo specializované ASIC čipy ztrácejí svou výhodu a prolomení hesel se stává prakticky neproveditelným.

Shrnutí

V dnešním článku jsme si ukázali postupný vývoj ukládání hesel ve webových aplikacích, od plaintextu, až po hashování. V moderních webových aplikacích se hesla doplňují o unikátní salt a do databáze se ukládá jen výsledný hash pomalé hashovací funkce (např. Argon2 nebo bcrypt). Tento postup chrání uživatele i v případě úniku databáze a je dnes považován za standardní a jediný bezpečný způsob ukládání hesel.