Dynamische salt
Maar met name bij een dynamische salt vraag ik me wel af wat de beste methode is en wat de nut van deze salt is. In mijn registratie-script maak ik een random salt aan en sla deze (zonder hash) op in de database, los van het wachtwoord (met hash).
Bij het inloggen zal ik de salt op moeten zoeken aan de hand van de inloggegevens van de gebruiker. Dit wordt alleen lastig omdat dat alleen kan met de gebruikersnaam, als deze uniek is.
<code>
$query_salt = mysql_query("
SELECT
salt_wachtwoord
FROM
users
WHERE
gebruikersnaam = '". $login_user ."'
",$connection);
$result_query_salt = mysql_fetch_array($query_salt);
$salt = $result_query_salt['salt_wachtwoord'];
$hashed_wachtwoord = sha1(trim($login_pass . $salt));
$query_checklogin = mysql_query("
SELECT
klantnummer,
gebruikersnaam
FROM
users
WHERE
gebruikersnaam = '". $login_user ."'
AND
hashed_wachtwoord = '". $hashed_wachtwoord ."'
",$connection);
</code>
(foutafhandeling / sql-injection weggelaten in dit voorbeeld)
Op het moment dat de gebruikersnaam niet uniek is werkt dit al niet meer.
Kan iemand vertellen of hier een betere manier voor is? En is het opslaan van een statische salt in de database niet net zo (on)veilig?
Gewijzigd op 10/01/2012 20:56:04 door Peter van den Dungen
Peter van den Dungen op 10/01/2012 20:43:08:
Dit wordt alleen lastig omdat dat alleen kan met de gebruikersnaam, als deze uniek is.
Moet er niet aan denken dat twee (of meer) personen dezelfde gebruikersnaam hebben. De kans is klein, maar ze zullen ook met hetzelfde wachtwoord hebben..... Dat risico moet je niet willen lopen.
En daarmee los je ook gelijk jouw probleem op: een unieke gebruikersnaam kun je een dynamische salt aan koppelen.
Gewijzigd op 10/01/2012 20:47:41 door Obelix Idefix
Is bovenstaand dus toch wel een goede optie met gebruik van een unieke gebruikersnaam?
Gewijzigd op 10/01/2012 20:50:27 door Peter van den Dungen
Het antwoord op je vraag is denk ik relatief simpel: 'Dwing unieke gebruikersnamen af!'.
Het is erg ongewenst om 'niet unieke' gebruikersnamen toe te staan. Jan != jan != jAn != jAN.
Het is ook gebruikerlijk om een username in kleine letters te gebruiken. Met de functie toLower() kun je de gebruikernamen naar LowerCase omzetten. Als je toLower() ook gebruikt voor je logins heb je ook geen probleem als je eens met een hoofdletter aanmeldt.
Maar wanneer een hacker toegang heeft weten te krijgen tot de database, en hij ziet de salt als plain text staan, kan hij deze toch gewoon verwerken in zijn brute-force, en is het hele nut van een salt toch weg? Kun je de salt dan ook niet veilig opslaan?
Ik stel voor dat je zowel een statische als dynamische salt gebruikt. De dynamische sla je op in de database bij de user, misschien zelfs voor/achter de hash. De statische sla je op in de source of een plek waar je met database toegang niet kunt komen maar met php wel.
Ik raad aan dat je een brcypt(hash_hmac(pass, statisch), dynamisch) hashing formaat gebruikt. Zie de crypt() functie voor bcrypt (gebruikt blowfish) of google naar bcrypt!
Gewijzigd op 12/01/2012 00:17:57 door Dos Moonen
Peter van den Dungen op 10/01/2012 20:49:25:
Hetzelfde wachtwoord is m.b.v. een dynamische salt geen optie lijkt me.
Als jij toestaat dat er meerdere keren een gebruikersnaam Jan is, hoe kun je dan de inloggegevens controleren? Welke dynamische salt moet er dan gebruikt worden? Of ga je de hele database door en probeer je elke combinatie van Jan met de bijbehorende dynamische salt?
De meest eenvoudige/praktische oplossing is: gebruikersnaam = uniek.
Misschien een rare vraag... maar hoe werkt zo'n dynamische salt eigenlijk? Wat moet ik me daar bij voorstellen?
uniqid kunnen gebruiken. Of zelf wat in elkaar knutselen doormiddel van hashes en tijd functies.
Ozzie, je zou met dynamische salt bijv. Gewijzigd op 11/01/2012 08:55:30 door Ozzie PHP
Een optie die ik eens bedacht heb is om een soort key te maken. Deze key is dynamisch en zit tussen de salt en het wachtwoord. Deze key heeft wel 1 ding altijd gemeen, bijv. eerst 2 letters dan 1 cijfer dan 1 letter en dan weer 2 cijfers. Deze verschillen dus per tijd, maar het heeft dezelfde opbouw. Vervolgens kun je dit eruit halen en hiermee de string breken in 2 stukken: De salt en het wachtwoord. Dit wachtwoord kun je vervolgens in een script gebruiken.
Als je dan een stap verder gaat met een salt, voeg je eigenlijk gewoon een string toe die zorgt dus dat die ander tabel niet meer bruikbaar is. Je moet immers dan een nieuwe genereren met de salt erbij. Maar als je die eenmaal gegenereerd hebt dan ben je dus weer de zak.
Met een dynamische salt, die je dus eigenlijk per gebruiker opslaat kun je die rainbow tabel ook PER gebruiker gaan genereren. En dan is de lol er snel af want dat kost nog veel te veel tijd (wordt wel steeds minder met de ontwikkelingen van steeds snellere rekenkracht van computers natuurlijk).
Het is dus eigenlijk gewoon een extra horde voor degene die de wachtwoorden buit wil maken en "hackers" zijn vaak lui. Er is immers ook ergens anders wel wat te vinden wat minder beveiligd is :)
Oké, ik snap het. Maaaar... als die database dan op straat komt te liggen... wordt het dan niet makkelijker aangezien per gebruiker keurig vermeld is wat zijn / haar salt is?
Bij één salt voor iedereen kom je er wel achter wat de salt is en als je die eenmaal hebt hoef je dus maar 2 rainbow tabellen te genereren, eentje om te achterhalen wat de salt is (als die niet al via een andere manier te achterhalen is) en daarna eentje om de tabel met die salt erbij te genereren.
Edit:
Dan zit je trouwens ook nog met, als je één salt gebruikt en ze genereren een tabel om die salt te achterhalen (wat ze misschien nog niet eens weten dat dat zo is) dan is de "beveiliging" daarvan net zo sterk als de kortste wachtwoorden.
En ook de hash methode is gewoon erg belangrijk, md5() is eigenlijk veel te snel. Voor jou maakt 20ms misschien niet zo veel uit, bij het genereren van een tabel kan dat jaren/eeuwen schelen.
En wat je ook zou kunnen doen, is voor de salts een aparte database te maken, met een andere user. Dan hebben ze aan een enkele database niet genoeg, maar hebben ze ze eigenlijk beide nodig.
Gewijzigd op 11/01/2012 09:44:43 door Jelle -
Iemand die handig is en beschikt over de database zou natuurlijk een stukje code maken die per gebruik de unieke salt pakt en dan met die unieke salt gaat ie dan de rainbow table afzoeken op een match, maar inderdaad... het wordt er dan wel al een stuk lastiger op. Maar beter lijkt me dan een combinatie van dynamische salt (opgeslagen bij de user in de database) en een vaststaande pepper die NIET in de databse staat maar in de code. Dit zou dan een string kunnen zijn die je zelf hebt verzonnen, of wellicht een gegenereerde string op basis van de url van de website (of iets dergelijks). Dus bijv. md5('www.mijnsite.nl').
Ander ideetje... zou het een idee zijn om op basis van het e-mailadres een salt te creëren? Dus stel je gooit het e-mailadres door een of andere functie... daar komt dan een stukje code uit die je dan als salt zou kunnen gebruiken. Om dan te gaan bruteforcen zou je moeten weten hoe de functie in elkaar zit, anders gaat het je nooit lukken. Zou dit een idee zijn?
p.s. mooie avatar smur f :)
Het nadeel: als men weet hoe de salt gegenereerd wordt dan kan men alsnog rainbow tables gaan maken.
Het voordeel (denk ik): omdat de salt gegenereerd wordt aan de hand van het mailadres hoef je de salt niet in de db op te slaan.
Is dit een goed idee?
Als ze er achter komen hoe die salts worden gegenereerd zijn ze niet veel verder dan als jij het gewoon in de database zou staan dus het zou in principe toch weer een extra blokkade zijn.
Wat bedoel je precies? Ik snap je zin niet helemaal. Is het wel of niet een goed idee volgens jou?
Stel je voor dat ze achter de functie komen en ze weten hoe jij de salts genereerd dan zijn ze nog niet veel verder dan als niet zon functie zou gebruiken en ze random genereerd en in de database opslaat.
Ook wel, het is wel een goed idee, maar dan moet je wel garanderen dan je rare tekens zoals ? en ^ etc er in zet. Dus het kan nog wel lastig zijn om die salts te maken maar het is geen slecht idee.