Ik ben bezig met het bouwen van een veilig inlogsysteem. Ik sta nu voor een keuze: Salt per sessie toevoegen + publieke salt per user, of geen salt per sessie toevoegen + geheime salt per user.
Hoewel hier vele topics over zijn, en ook op Google veel is te vinden, zijn de meningen erg verdeeld. Ik zie zelf veel voordelen in een salt per sessie, maar dan is volgens mij de consequentie dat de user-salt publiekelijk bekend wordt. Soms lees je dat dit absoluut niet acceptabel is, sommige bronnen zeggen dat het niets uit maakt.
Wat ik nu heb gemaakt:
- Iedere user heeft een salt
- Deze salt wordt naar de cliënt gestuurd als een bepaalde user wil inloggen of een wachtwoord wil wijzigen
- Cliënt side worden wachtwoord en salt gecombineerd, SHA512 gecodeerd en meegestuurd met het login formulier. Uiteraard wordt het plain-text wachtwoord dat de user heeft ingevoerd niet mee gestuurd. Dit is doorgevoerd in alle dialoogvensters/pages waar een user een wachtwoord opgeeft (login, wachtwoord keuze bij registratie, wachtwoord keuze via gemailde wachtwoord-restore-link en wachtwoord wijzigen).
- Serverside is het momenteel heel simpel: Meegestuurd gecodeerde wachtwoord wordt vergeleken met wat er in de database staat.
Al een hele verbetering t.o.v. wat ik eerder had, namelijk formulieren die plain-text verstuurd werden en wachtwoord hashes obv. MD5 zonder salt.
Maar het kan beter. De huidige situatie is nog gevoelig voor man-in-the-middle attacks waarbij simpelweg het gecodeerde wachtwoord wordt gestolen en naar hartelust kan worden gebruikt om in te loggen.
Nog een nadeel aan de huidige situatie: User-salt is bekend omdat deze mee wordt gestuurd naar de cliënt.
Ik kan naar mijn idee twee kanten uit:
1. Per sessie een salt meesturen, die na elke loginpoging niet meer geldig is. Hierdoor wordt een hash die door sniffing is verkregen onbruikbaar.
Ik gebruik dan bijvoorbeeld SHA512(sessie_salt+SHA512(user_salt+user_pass)) als hash.
Sessie-salt wordt uiteraard naar de cliënt gestuurd evenals de user-salt. Die sessie-salt vervalt na elke login (succesvol en onsuccesvol).
Server-side wordt een hash op basis van SHA512(user_salt+user_pass) uit de database gehaald, vervolgens SHA512(sessie_salt+dbhash) en dat wordt vergeleken met de hash die vanuit de cliënt komt. Ongeacht de uitkomst van die vergelijking wordt de sessie_salt vernietigd en eventueel opnieuw random gegeneerd voor een volgende poging.
Voordeel: Een onderschepte hash is niet te gebruiken.
Nadeel: User salt openbaar
2. De salt per user geheim houden.
Nu stuur ik die mee.
Als ik die geheim hou, dan stuurt de client een hash op basis van SHA512(user_pass), eventueel met een vaste salt er bij om bestaande dictionaries onbruikbaar te maken.
Server-side wordt een hash op basis van SHA512(user_salt+SHA512(user_pass)) uit de database gehaald, vervolgens wordt de hash vanuit de cliënt verder gecodeerd met SHA512(user_salt+cliënt_hash) en vergeleken met de hash in de database.
Voordeel: User salt niet openbaar
Nadeel: Als de hash wordt onderschept kan hiermee ingelogd worden (wachtwoord is gelukkig wel onbekend).
Naar mijn idee is het niet mogelijk om keuze 1 en 2 zo te combineren dat de nadelen vervallen en alle voordelen gelden. Volgens mij is het onmogelijk om een user salt toe te passen in combinatie met een salt per sessie zonder die user salt mee te sturen naar de cliënt.
Maar misschien zie ik iets over het hoofd.
Wat is de beste keus?
Ik neig naar keuze 1. De kans dat de database in verkeerde handen valt is kleiner dan dat een meegestuurde hash wordt onderschept.
N.B. Deze vraag staat los van het gebruik van SSL. Uiteraard heb je met een beveiligde verbinding het probleem niet meer dat de hash onderschept kan worden, en kan is keuze 2 het meest aantrekkelijk.
Hoewel hier vele topics over zijn, en ook op Google veel is te vinden, zijn de meningen erg verdeeld. Ik zie zelf veel voordelen in een salt per sessie, maar dan is volgens mij de consequentie dat de user-salt publiekelijk bekend wordt. Soms lees je dat dit absoluut niet acceptabel is, sommige bronnen zeggen dat het niets uit maakt.
Wat ik nu heb gemaakt:
- Iedere user heeft een salt
- Deze salt wordt naar de cliënt gestuurd als een bepaalde user wil inloggen of een wachtwoord wil wijzigen
- Cliënt side worden wachtwoord en salt gecombineerd, SHA512 gecodeerd en meegestuurd met het login formulier. Uiteraard wordt het plain-text wachtwoord dat de user heeft ingevoerd niet mee gestuurd. Dit is doorgevoerd in alle dialoogvensters/pages waar een user een wachtwoord opgeeft (login, wachtwoord keuze bij registratie, wachtwoord keuze via gemailde wachtwoord-restore-link en wachtwoord wijzigen).
- Serverside is het momenteel heel simpel: Meegestuurd gecodeerde wachtwoord wordt vergeleken met wat er in de database staat.
Al een hele verbetering t.o.v. wat ik eerder had, namelijk formulieren die plain-text verstuurd werden en wachtwoord hashes obv. MD5 zonder salt.
Maar het kan beter. De huidige situatie is nog gevoelig voor man-in-the-middle attacks waarbij simpelweg het gecodeerde wachtwoord wordt gestolen en naar hartelust kan worden gebruikt om in te loggen.
Nog een nadeel aan de huidige situatie: User-salt is bekend omdat deze mee wordt gestuurd naar de cliënt.
Ik kan naar mijn idee twee kanten uit:
1. Per sessie een salt meesturen, die na elke loginpoging niet meer geldig is. Hierdoor wordt een hash die door sniffing is verkregen onbruikbaar.
Ik gebruik dan bijvoorbeeld SHA512(sessie_salt+SHA512(user_salt+user_pass)) als hash.
Sessie-salt wordt uiteraard naar de cliënt gestuurd evenals de user-salt. Die sessie-salt vervalt na elke login (succesvol en onsuccesvol).
Server-side wordt een hash op basis van SHA512(user_salt+user_pass) uit de database gehaald, vervolgens SHA512(sessie_salt+dbhash) en dat wordt vergeleken met de hash die vanuit de cliënt komt. Ongeacht de uitkomst van die vergelijking wordt de sessie_salt vernietigd en eventueel opnieuw random gegeneerd voor een volgende poging.
Voordeel: Een onderschepte hash is niet te gebruiken.
Nadeel: User salt openbaar
2. De salt per user geheim houden.
Nu stuur ik die mee.
Als ik die geheim hou, dan stuurt de client een hash op basis van SHA512(user_pass), eventueel met een vaste salt er bij om bestaande dictionaries onbruikbaar te maken.
Server-side wordt een hash op basis van SHA512(user_salt+SHA512(user_pass)) uit de database gehaald, vervolgens wordt de hash vanuit de cliënt verder gecodeerd met SHA512(user_salt+cliënt_hash) en vergeleken met de hash in de database.
Voordeel: User salt niet openbaar
Nadeel: Als de hash wordt onderschept kan hiermee ingelogd worden (wachtwoord is gelukkig wel onbekend).
Naar mijn idee is het niet mogelijk om keuze 1 en 2 zo te combineren dat de nadelen vervallen en alle voordelen gelden. Volgens mij is het onmogelijk om een user salt toe te passen in combinatie met een salt per sessie zonder die user salt mee te sturen naar de cliënt.
Maar misschien zie ik iets over het hoofd.
Wat is de beste keus?
Ik neig naar keuze 1. De kans dat de database in verkeerde handen valt is kleiner dan dat een meegestuurde hash wordt onderschept.
N.B. Deze vraag staat los van het gebruik van SSL. Uiteraard heb je met een beveiligde verbinding het probleem niet meer dat de hash onderschept kan worden, en kan is keuze 2 het meest aantrekkelijk.