Det är otroligt vanligt med online-tjänster som erbjuder att nollställa ett glömt lösenord genom att skicka en engångs-länk till den e-postadress man registrerade i samband med att kontot skapades. Det är nästan lika vanligt med lösningar som är ganska komplicerade att implementera – helt i onödan. Först kommer ett exempel på hur man kanske intuitivt skulle bygga en sådan funktion, och därefter en mycket enklare och elegantare lösning.
Några grundläggande säkerhetskrav behöver vi alltid ha med oss, oavsett hur vi väljer att implementera lösningen:
Med ovanstående säkerhetskrav på lösningen kommer en erfaren utvecklare ganska lätt komma fram till en fungerande lösning:
https://example.com/resetPassword?id=F39C0A16BC653B5DF39C0A16BC653B5D
När användaren fått e-postmeddelandet och klickar på länken kommer sedan följande process att köras:
För att inte databasen ska skräpas ned av gamla biljetter för att nollställa lösenord bör man också införa någon typ av batch-jobb som periodiskt rensar bort gamla biljetter
Ganska mycket logik för en liten funktion. Har man dessutom en klustrad miljö behöver man säkerställa att batchjobben startas på endast en nod i klustret.
Det vore inte så dumt om man kunde få till ovanstående utan att behöva hålla koll på tillstånd på serversidan och i databasen, och om man inte hade några som helst säkerhetsaspekter att ta hänsyn till skulle man kunna skicka en nollställningslänk som ser ut på följande sätt:
https://example.com/resetPassword?e=name@example.com&issued=201710021T10101Z
Vi skickar alltså i klartext alla parametrar, och slipper hålla koll på dem på serversidan. Det betyder ju förstås samtidigt att vem som helst som kan läsa mönstret också skulle kunna ändra på informationen och byta lösenord för vilken användare som helst, så det duger inte i verkligheten.
Hur kan man säkerställa att ingen kan ändra meddelandet? Det finns en beprövad metod för detta – en digital signatur. Om vi bakar ihop alla parametrar och signerar dem och sedan hakar på signaturen på slutet kan vi direkt detektera att meddelandet inte är manipulerat:
https://example.com/resetPassword?e=name@example.com&issued=20171002T110101Z&sign=F39F39CC0A16
Det man kan anmärka på är att det verkligen är att skjuta mygg med kanoner att använda sig av en digital signatur för att verifiera ett meddelande man själv skapat. Man ska hålla reda på både en publik och en privat nyckel, och den underliggande algoritmen är beräkningsintensiv. Borde man inte kunna göra det enklare? Jo – det finns en teknik även för detta: HMAC, eller Hashed Message Authentication Code. Lite förenklat innebär HMAC att man slår ihop ett meddelande med en hemlig nyckel och producerar en hash av totalen. Att verifiera integriteten i ett tidigare ”signerat” meddelande görs genom att räkna fram en ny HMAC och jämföra med den som följer med meddelandet. Om de är lika är innehållet heller inte manipulerat.
Hur gör vi för att förhindra att samma länk används flera gånger? Ett sätt att göra det på är att ta med användarens lösenords-hash i beräkningen av HMAC-signaturen. Om användaren redan har ändrat sitt lösenord kommer det att bli en annan HMAC vid verifieringen. Observera att man inte ska skicka med själva lösenordshashen i e-postmeddelandet.
Den nya lösningen kan nu beskrivas enligt följande:
https://example.com/resetPassword?e=name@example.com&issued=20171002110101&mac=EB9C1AD6416F3CFD139C0A1BA76E311FE
När användaren fått e-postmeddelandet och klickar på länken kommer sedan följande process att köras:
Med det är vi färdiga. Inget behov av ny information i databasen, och därför heller inget behov att rensa gamla biljetter.
Förhoppningsvis visar exemplet ovan att det finns en del att tjäna på att använda sig av HMAC för självbetjäning kring bortglömda lösenord. Användningsområdet är dock bredare än så. Generaliserat kan man använda HMAC för att säkerställa att information som lämnats ut inte har förändrats innan den skickas tillbaka.