Hashing-processen (i modsætning til kryptering) producerer et output fra input, hvorfra den oprindelige streng ikke længere kan udledes.
Den er derfor velegnet til beskyttelse af følsomme strenge, adgangskoder og checksummene.
En anden god egenskab ved hashing-funktioner er, at de altid genererer output af samme længde, og at en lille ændring i input altid ændrer hele outputtet fuldstændigt.
Der er mange hash-funktioner i PHP, de vigtigste er:
$password = 'secret-password';echo password_hash($password); // Bcryptecho md5($password);echo sha1($password);
Varsling: Hverken
md5()
ellersha1()
er egnet til hashing af adgangskoder, fordi det er beregningsmæssigt let at finde frem til den originale adgangskode eller i det mindste at forudberegne adgangskoderne. Det er meget bedre at brugebcrypt
, som blev udviklet til hashing af adgangskoder.md5cracker.com har en database med checksum (hashes), prøv at søge efter hash:
79c2b46ce252594ecbcbcb5b73e928345492
, som du kan se, så renmd5()
er ikke så sikkert for almindelige ord og adgangskoder.
Bcrypt + salt
.I foredraget Sådan laver du ikke rod i målplanet, behandlede David Grudl måder at hashe og gemme adgangskoder på korrekt vis.
Den eneste korrekte løsning er: Bcrypt + salt
.
Mere specifikt:
$password = 'hash';// Genererer en sikker hashecho password_hash($password, PASSWORD_BCRYPT);// Alternativt med højere kompleksitet (standard er 10):echo password_hash($password, PASSWORD_BCRYPT, ['omkostninger' => 12]);
Fordelen ved Bcryp er primært dens hastighed og automatiske saltning.
Det faktum, at det tager langt at generere, f.eks. 100 ms, gør det meget dyrt for en angriber at teste mange adgangskoder.
Desuden behandles output-hashingen automatisk med tilfældig salt, hvilket betyder, at når det samme kodeord hashes gentagne gange, er output altid en anden hash. Derfor vil en angriber ikke kunne bruge en forudberegnet hashtabel.
Derfor vil vi ikke kunne verificere, at adgangskoden er korrekt ved gentagen hashing, men skal kalde en specialiseret funktion:
if (password_verify($password, $hash)) {// Adgangskoden er korrekt} else {// Adgangskoden er forkert}
For at gøre det vanskeligere at knække hash-koder er det en god idé at indsætte en ekstra streng i det oprindelige input. Helst en tilfældig. Denne proces kaldes password salting.
Sikkerheden er baseret på den idé, at en angriber ikke vil kunne bruge en på forhånd beregnet tabel med adgangskoder og hashes, fordi han ikke kender saltet og skal knække adgangskoderne enkeltvis.
For eksempel:
$password = 'secret_passport';$salt = 'fghjgtzjjhg';$hash = md5($password . $salt);echo $password; // udskriver den oprindelige adgangskodeecho $hash; // udskriver hash af adgangskode inklusive salt
Du tænker måske, at det ville være en god idé at udføre hash-funktionen flere gange og dermed gøre det mere kompliceret at knække den, da det oprindelige kodeord skal hashes flere gange.
For eksempel:
$password = 'adgangskode';for ($i = 0; $i <= 1000; $i++) {$password = md5($password);}echo $password; // 1000x hashed via md5()
Paradoksalt nok bliver det mindre vanskeligt at bryde igennem eller forbliver næsten uændret.
Årsagen er, at funktionen md5()
er ekstremt hurtig og kan beregne over en million hashes i sekundet på en almindelig computer, så det er ikke meget langsommere at prøve adgangskoderne en efter en.
Den anden grund er mere teoretisk, nemlig muligheden for at støde på en såkaldt kollision. Hvis vi hasher et kodeord gentagne gange, kan det med tiden ske, at vi finder en hash, som angriberen allerede kender, og det vil gøre det muligt for ham at hashe kodeordet ved hjælp af databasen.
Derfor er det bedre at bruge en langsom, sikker hashing-funktion og kun udføre hashing-funktionen én gang, mens det endelige output stadig behandles med saltning.
Vidste du, at ===-operatoren ikke er det mest sikre valg til hash-sammenligning i forbindelse med adgangskodebekræftelse?
Når du sammenligner strenge, gennemløbes de to strenge tegn for tegn, indtil du når slutningen (succes, de er ens) eller der er en forskel (strengene er forskellige).
Og dette kan udnyttes i et angreb. Hvis du måler tiden nøjagtigt nok, kan du vurdere, hvor mange tegn der skal tilføjes for at få et nøjagtigt match og nå til slutningen, eller du kan vurdere, hvor langt strengene er nået, når du sammenligner strengene.
Løsningen er at bruge funktionen hash_equals() overalt hvor strenge sammenlignes, og det ville gøre noget, hvis en angriber kunne finde ud af, hvor sammenligningen mislykkedes.
Og hvordan gør funktionen det? Den sørger for, at sammenligningen af to strenge altid tager lige lang tid, så du ikke kan se ved at måle tiden, hvor forskellen er opstået. Jeg finder nogle typer angreb virkelig meget usandsynlige og svære at gennemføre. Dette er en af dem.
Jan Barášek Více o autorovi
Autor článku pracuje jako seniorní vývojář a software architekt v Praze. Navrhuje a spravuje velké webové aplikace, které znáte a používáte. Od roku 2009 nabral bohaté zkušenosti, které tímto webem předává dál.
Rád vám pomůžu:
Články píše Jan Barášek © 2009-2024 | Kontakt | Mapa webu
Status | Aktualizováno: ... | da