Lösenordshantering - MD5 och salt

Från Wikibooks

När man pratar om lösenord så brukar man säga att det är användarens skyldighet att välja ett långt och säkert lösenord, och det är helt sant, till viss del. Men du som utvecklare har också ett stort ansvar i det hela, du måste se till att lösenorden sparas i databasen på ett säkert sätt. Om någon utomstående skulle lyckas ta sig in i din databas så vore det rena rama julafton för honom/henne om alla lösenord är sparade i klartext. Det är väldigt viktigt att du på något sätt krypterar sparade lösenord, och i den här artikeln har jag tänkt att gå igenom ett effektivt sätt.

Att tänka på: Även om din sida inte är så populär och det inte gör så mycket om databasen blir stulen så kan dina medlemmar ha samma lösenord på andra sidor, som är betydligt viktigare. Ge dina besökare den säkerhet de förtjänar.

Kryptering har funnits sedan urminnes tider och innebär att man döljer ett meddelande för obehöriga läsare, och det är ju precis så vi vill ha det med våra lösenord, eller hur? Hur ska vi då bära oss åt för att få ett så bra skydd som möjligt?

Lösenorden får alltså inte sparas i klartext, för då kan de läcka ut. De bör krypteras på något sätt, men om de skall dekrypteras så kan ju nyckeln läcka. Lösningen är envägskrypteringar: kryptografiska hash-funktioner. Med dem dekrypterar man inte det sparade lösenordet för att jämföra med det som matas in, utan kör tvärtom det inmatade lösenordet genom samma funktion och kollar om resultatet blev lika som det sparade. Det skall vara just en kryptografisk hash-funktion, för annars är det sannolikt att lösenorden går att få fram på lättare sätt.

Vi ska använda oss av en envägskryptering som kallas MD5, i PHP som vi kommer att använda oss av i den här artikeln, finns funktionen inbyggd som md5().

MD5 är, som sagt, en envägskryptering, eller hash-funktion som det egentligen heter. För att beskriva en hash-funktion på ett enkelt sätt så kan du tänka dig att vi ska kryptera sifferkoden 12345. Vi adderar ihop alla tal i koden: 1 + 2 + 3 + 4 + 5 = 15. Nu har vi fått "hash-summan" 15 genom lite enkel matematik. Men det är inte lika enkelt att gå åt andra hållet. Vi kan inte utgå från summan 15 och lista ut vilka tal vi adderade för att komma fram till 15 (men det här är inte en kryptografisk hash-funktion, resultatet blir samma med t.ex. 2+2+3+3+5).

Okej, då sätter vi igång med det praktiska. Vi tänker oss en registreringssida där besökarna ska välja ett lösenord. Du hämtar lösenordet som vanligt och sparar det i en variabel. Men innan du sparar lösenordet i databasen måste du köra den genom MD5-funktionen.

<?php
$password = $_POST['password'];
$md5_password = md5($password);

// Spara $md5_password i databasen
?>

Nu kommer det hashade lösenordet att sparas i databasen. Inte så svårt, eller hur? Bra! Men hur ska vi nu bära oss åt när vi ska kontrollera om det är rätt lösenord vid inloggningen? Ta det lugnt, det är inte svårt alls. Vi gör bara samma sak igen. Vi hämtar lösenordet som användaren skriver in, kör det genom MD5-funktionen och kollar sedan om det är detsamma som det sparade hash-värdet i databasen.

  • Vid registrering: Hämta inskrivet lösenord » Kör det genom MD5-funktionen » Spara i databasen
  • Vid kontroll: Hämta inskrivet lösenord » Kör det genom MD5-funktionen » Jämför med det sparade lösenordet

Nu är lösenorden bra mycket säkrare, men de går faktiskt fortfarande att knäcka. Men vadå? Jag sa ju att det inte gick att dekryptera hashade lösenord. Det är helt sant, det går inte. Men det finns personer som har samlat på sig stora listor med hashade ord. Dessa listor innehåller miljontals ord och teckenföljder. Med hjälp av dessa ordlistor kan hackare jämföra dina lösenordshashar med hasharna i ordlistan, och på så sätt hitta rätt lösenord.

För att skydda lösenordet ytterligare måste vi därför använda oss av något som kallas salt. Att "salta" ett lösenord innebär att man lägger till några slumpade tecken före och/eller efter lösenordet innan man kör det genom MD5-funktionen. På så sätt är det nästintill omöjligt för hackarna att hitta lösenordshashen i en ordlista. Här är en metod för att salta ett lösenord:

<?php
$password = $_POST['password'];
$salt1 = "18gI%f5A";
$salt2 = "@Y4p91bN";
$salt_password = md5($salt1.$password.$salt2);

// Spara $salt_password i databasen
?>

Om en användare nu skulle valt att använda, till exempel, "internet" som lösenord, som vanligen skulle finnas med i en ordlista, skulle "ordet" 18gI%f5Ainternet@Y4p91bN köras genom MD5-funktionen istället och sparas i databasen. Självklart måste man lägga till salt-nycklarna när man ska kontrollera lösenorden vid inloggning också. Saltet borde skapas med en slumptalsgenerator och måste då sparas tillsammans med lösenordshashen.

Dåliga lösenord kan ändå gissas[redigera]

Saltet gör alltså färdigt hashade lösenordslistor opraktiska, då de borde göras upp för alla tänkbara salt (eller för de salt som verkligen används, en rätt använd bra slumptalsgenerator behövs för att det skall vara samma sak). Däremot är det inget som hindrar en angripare att pröva en lösenordslista genom att generera lösenordshashen för varje ord med hjälp av det använda saltet. Att pröva miljarder lösenord på det sättet är jobbigt, men några miljoner kan man kosta på sig. Lösenorden bör alltså inte vara bland de första miljonerna angriparen testar. Det betyder att de inte skall vara enkla varianter på ord eller fraser som finns i någon som helst ordlista eller annan tabell. Om samma salt används för flera lösenord kan dessa lösenord testas på samma gång, utan extra ansträngning.

Hur undviker man att användarna väljer dåliga lösenord? Ett sätt är att inte godkänna lösenord som är alldeles uppenbart dåliga (en snabb test genast). Ett annat att ge goda råd. Ett tredje är att inte ställa orimliga krav (som leder till att användarna hittar på sätt att fuska), utan låta användaren välja ett bra lösenord och behålla det lång tid. Hur som helst visar det sig att en absurt hög andel av lösenorden ändå är dåliga. Dessa kan hittas med lösenordsknäckarprogram, så att man kan be folk välja ett nytt, bättre lösenord.