Jelszavak helyes tárolása

Tegnap történt, hogy sok év után elfelejtettem a Tumblr jelszavamat. Ez úgy történhetett meg, hogy ott már soha nem használok olyan jelszót (azt a jelszó alkotási taktikát), amit más oldalakon. Mivel nem bízok benne. Így, amikor új jelszót akartam generálni egy olyan üzenet fogadott, hogy “ezt a jelszót már használtad egyszer”.

Miért baj ez?

Nem feltétlenül az. De ismerve a fejlesztői kultúrát, a rengeteg kiszivárgott jelszót, nem tudok egyből a jóra gondolni. Amennyiben egy rendszer meg tudja állapítani, hogy mely jelszót használtuk már, erős lehet a gyanúnk, hogy nem helyesen, hash formájában tárolják azt, hanem legjobb esetben is csak rejtjelezve. Ami vissza bontható és szivárgás esetén nem csak a jelenlegi jelszavunk, hanem minden előző jelszó is kiszivárog.

Persze nem biztos, hogy így oldották meg. Van erre egy helyes és teljesen biztonságos megoldás, amire emiatt jöttem rá.

Hogy lehet ezt jól csinálni?

Először is elismétlem ezredszer, hogy miként kell simán jól tárolni jelszót. PHP nyelvet veszem alapnak, mivel legtöbb web appnak köze van hozzá úgyis.

Szabályok

  1. Senki nem férhet hozzá a jelszóhoz. Mindegy mi a felhasználó jelszava, úgy kell tárolni, hogy ne lehessen következtetni arra. Se annak, aki látja az adatbázist, se annak aki ellopja azt. Ezért kell hash-elni.
  2. Nem szabad, hogy egyforma jelszavak egyforma hash-t alkossanak.

Megoldás

Hash + egyedi só a jó megoldás.

A hash algoritmusok lényege az, hogy egy adott szöveg, mindig ugyan azt a szám és betűsort generálja. Például a “kacsa” szó md5 hash értéke mindig “145dd1ae7c8600bd538a0f3a6141b7b5” lesz. De, ha egy betűt is megváltoztatunk benne, akár így: “kAcsa”, máris egészen más lesz “1aa924eb48d66933557be0bcb9d129f9”. De visszafejteni nem lehet őket. Jelszavak esetében ez nem elég. Ha két ember is ugyan azt a jelszót használja (ami nagyon esélyes), akkor adatbázisban egyforma hash értéket fogunk látni. Ez máris információval szolgál, hogy kik használják ugyan azt a jelszót, így ha valahonnan máshonnan megszerzik egyik célpont jelszavát, máris az összes többié is megvan.

Itt jön be a képbe a sózás (angolul salt-nak hívják a módszert). Ez abból áll, hogy minden jelszó mellé elmentünk egy só értéket is, ami lehetőleg teljesen egyedi. Minden jelszó váltásnál újat kell generálni belőle. Így, amikor hash-eljük a jelszót a sót hozzá fűzzük hash-elés előtt. Például így:

//új jelszót hozunk létre
 
$so       = hash('sha256' , mt_rand().time()); //véletlen só a jelenlegi időből és md_rand értékből. Így biztosan egyedi
$jelszo = hash('sha256' , $so . $_POST['jelszo'] . $so); //a lényeg, hogy berakjuk sót és a jelszót is ugyan abba a hash-be. 
 
mentes($jelszo , $so); //nyilván a példában nem hozok létre teljes funkciót is adatbázis mentésre :D Kell egy jelszo és egy so mező az adatbázisba

Ezután, amikor be akarjuk léptetni a felhasználót ezt a kódot használhatjuk:

if($adatbazisbolHash == hash('sha256' , $adatbazisbolSo . $_POST['jelszo'] . $adatbazisbolSo))
{
 
   $belephet = true;
 
}

Nyilván egyszerűbb az egész jelszó hash-elő algoritmust egy funkcióba rakni, például:

function hashJelszo($jelszo,$so)
{
 
return hash('sha256',$so.$jelszo.$so);
 
}

Persze még jobb osztályt létrehozni hozzá. Mint például ez az osztály, amit évekkel ezelőtt írtam.

Hogyan lehet megállapítani, hogy adott jelszót használta-e már a felhasználó?

Ez az, ami miatt ezt a tutorial szerűséget el kezdtem írni. Ez valami, amire most jöttem rá, mivel soha nem kellett ilyen rendszert írnom így nem is gondolkoztam rajta.

A megoldás pofon egyszerű, bár erőforrás igényesebb.

Létre lehet hozni egy “oldPasswords” táblát az adatbázisban, ami valahogy így nézhet ki: oldPasswordID (int, ai), oldPasswordHash (varchar(64)), oldPasswordSalt(varchar(64)), userName (varchar(x), indexed).

A jelenlegi hash párost lehet a felhasználó adatai közt tárolni a gyorsabb elérés érdekében. Viszont, amikor új jelszót szeretne létrehozni az userName alapján végig lehet pörgetni a beírt jelszót az összes hozzá tartozó hash pároson és így meg lehet találni azt, amelyikre illik. Így a jelszavak továbbra is biztonságban vannak, de elkerülhető, hogy a felhasználó ugyan azt a jelszót adja meg megint.

Miért ne használhatná egy régebben használt jelszavát?

Természetesen az is egy út. De biztonságosabb, ha nem engedjük. Bármelyik másik weboldalon lehetett szivárgás, amiben lehet, hogy pont az a jelszava volt benne, amit most újra megadna. Ezzel a technikával elkerülhetjük, hogy egy listázott, ismert jelszóval belépjen. Persze ebből az is adódik, hogy ha nagyon túl akarjuk tolni a biztonságot,akár több százmilliós szivárvány listát rakunk a rendszer mögé, amit minden jelszó létrehozásnál ellenőrzünk, hogy nincs-e benne. Amennyiben benne van értesítjük a felhasználót, hogy ez a jelszó nem biztonságos, hiszen szabadon kint van a neten már. De erre senkinek nincs erőforrása.

Zárás

Évtizedek után is folyamatosan szivárognak ki jelszavak nyers szöveg vagy könnyen feltörhető rejtjelezett formájában. Nem csak kicsi, jelentéktelen homebrew oldalakról van szó, hanem nagy, több millió felhasználóval működő óriások is elhibázzák a legalapvetőbb dolgokat kérdéskörben. Pedig a megoldás tényleg pofon egyszerű. A gond az, hogy meglévő rendszert kicsit nehezebb erre átállítani. Bár egyáltalán nem lehetetlen. Szóval hajrá.

+Tipp

Ha egy új weboldalra regisztrálunk első dolgunk az legyen, hogy valami random jelszót adjunk meg. Ezután azonnal nyomjunk rá az “elfelejtett jelszó” linkre. Ha a weboldal elküldi nekünk a beírt jelszót tudhatjuk: nyers szövegként tárolják a jelszavunkat és minden admin és tolvaj hozzáfér. Ami email címünkkel párosítva maga a pokol.

 

I don't speak Hungarian. Google translate it for me!