Programmera i PHP/Kryptering och PHP


OBS denna text är direktkopierad från sidan: http://sv.wikibooks.org/wiki/L%C3%B6senordshantering_-_MD5_och_salt eftersom det är bättre att den ligger under läroboken om PHP då den visar hur man hanterar kryptering i just det programmeringsspråket. Dessutom finns det två inbyggda krypteringar till i PHP: Base64 och sha1 som inte tas upp i originaltexten men som bör beskrivas här. De lägger jag till senare.


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å optimalt skydd som möjligt? Jo, det finns många olika krypteringsmetoder, men de flesta går på något sätt att dekryptera.

Men det finns också så kallade envägskrypteringar, som bara går att kryptera och inte dekryptera, det är en sådan kryptering vi ska använda oss av. 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.

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å gissasRedigera

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, 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.


SHA1Redigera

Då det visat sig att md5 kunnat knäckas med hjälp av regnbågstabeller, dvs listor med redan krypterade lösenord, finns det stöd för sha1 i php numera. Skillnaden är inte så enorm jämfört med md5. Man skriver koden exakt likadant som i exemplet här ovanför. Resultatet blir dock svårare att knäcka. Värdet på något man använt sha1 på blir alltid precis 40 tecken långt, i en databas skall fältet alltid vara 40. t.ex:

password varchar(40),

när man skapar fältet i MySQL. Eftersom fältet kan ha samma värde med olika lösenord kan man inte flagga det som att värdet måste vara unikt.

BASE 64Redigera

Både md5 och sha1 använder algoritmer där det sparade värdet i databasen är förstört. Det går inte längre att få fram, vilket ju också är meningen. Men om man vill kunna ha ett enkelt "krypteringssystem", vad finns det då för lösning?

Ett system kallas base 64. Ursprungligen togs det fram för att man skulle kunna koda om meddelanden och bilagor i e-post, när man skickade meddelanden via 7 bitars servrar. För att inte tecken med 8 bitar skulle förstöras kodades allt om till base 64 (där varje enhet av 3*8 bitar kodas med 4*6 bitar) eller quoted printable (där text med enstaka 8 bitars tecken förblev mer eller mindre läslig).

Ett exempel på en enkel variant om man vill använda sig av systemet är:

<?php 
function base64_url_encode($input)
{
    return strtr(base64_encode($input), '+/=', '-_,');
}
 
function base64_url_decode($input)
{
    return base64_decode(strtr($input, '-_,', '+/='));
} 
?>

Base64 tillåter alltså att man t.ex. sparar binära filer i textform i en databas och det går att skicka dem som information med ett e-post till någon som kan dekoda det utan salt-hash och återställa filen eller filerna.

Detta handlar dock inte om kryptering. Hur man återställer texten är allmänt känt, och base64 är såpass ofta använd att också många som inte vet att just den kodningen använts med hög sannolikhet kommer att pröva den.

KrypteringRedigera

För riktig kryptering behövs en nyckel som är tillräckligt svårgissad och en algoritm som inte har svagheter som gör att nyckeln eller det krypterade materialet kan fås fram på andra sätt än genom gissning eller av nyckelinnehavarna. Vanliga algoritmer är Twofish och AES. Nyckeln är vanligen genererad med en slumptalsgenerator och 128 bitar lång eller längre (drygt 20 tecken eller mer). Nyckeln förvaras ofta i krypterad form eller genereras skilt för en viss överföring. För överföring mellan två personer används ofta en mer resurskrävande asymmetrisk kryptering, traditionellt RSA, medan nyckelfiler kan krypteras med normala symmetriska algoritmer men med ett lösenord som går att minnas och då kan vara svagare än de slumpgenererade.

Ett problem med att ha krypterad känslig information (nycklar eller filer) på en server är att den måste dekrypteras innan användning. Detta är inget problem om användaren dekrypterar informationen efter att ha överfört den till egen dator, men om informationen skall hanteras på servern måste nycklarna finnas tillgängliga där och därmed kunna stjälas vid en attack. En möjlighet är att nycklarna inte lagras där, utan matas in av administratorn vid uppstart eller av användare inför att de skall användas. Då kan man undvika att ha dem i klartext på hårddisken, men fortfarande kan nycklarna eller den känsliga informationen avslöjas i attacker som kommer åt att läsa dem i minnet – och om man varit oförsiktig i temporära filer på hårddisken.