In principe is het niet verplicht dat het een hash is. Zolang je maar een indicatie hebt om mee na te gaan of de gebruiker inderdaad het wachtwoord kende. De vereisten aan die indicatie zijn uiteraard aanvullend dat het niet-triviaal moet zijn om een wachtwoord eruit terug te leiden, een van de eigenschappen van hashing-algoritmen.
Er zijn bijvoorbeeld ook methodes onder de noemer "zero-knowledge password proof" waarbij het wachtwoord uberhaupt nooit bij de serverzijde bekend wordt, enkel afgeleide bewijzen (evt deels aan de hand van een challenge-response). Het nadeel is dat ze over het algemeen een stuk werk aan de client-side verwachten, waarbij met name Javascript de beschikbare cryptografische rekenkracht nog vrij sterk beperkt.
Dus je mist eigenlijk nog een eis bij je wachtwoordopslag:
- Het moet zodanig opgeslagen worden, dat onomstotelijk is te bewijzen dat de gebruiker inderdaad het wachtwoord kende, zonder dat de daarvoor opgeslagen data omkeerbaar is naar het originele wachtwoord. Oftewel, dat enkel brute-forcen overblijft om het originele wachtwoord te achterhalen.
- Het moet gebruik maken van een willekeurige salt om te voorkomen dat een methode met voorberekende hashes (zoals rainbow tables) effectief ingezet kan worden.
Dat hangt helemaal van het gebruikte principe af. Maar je hebt gelijk dat als er gebruik van hashes of vergelijkbare eenmalige versleutelingen wordt gemaakt, dat het dan verstandig is de boel zodanig toe te passen dat brute-forcen (en voor-genereren voor rainbow tables) enkel op losse keys mogelijk is, niet op een hele reeks. Bij hashing wordt dat inderdaad opgelost met salts.
- Het moet een langzaam hashing algoritme gebruiken. Hierdoor valt het alleen toepassen van MD5 of SHA-1 al direct af. Deze algoritmen zijn zo snel dat het brute force genereren van alle mogelijke combinaties (salt of niet) met wat GPU-kracht zo gepiept is (we praten hier afhankelijk van de gebruikte tekenreeks over minutenwerk).
Eigenlijk is de eis hierbij iets anders

Het gebruik van brute-forcing is door de eerste eis de enige manier om het origineel te achterhalen. Om te voorkomen dat ontdekte codes in reele tijd terug te halen zijn, moet het algoritme relatief rekenintensief zijn. En bij hashes - die bedoeld zijn om snel berekend te worden (ook de hele sha2-reeks) - betekent dat dat je bijvoorbeeld kan "key stretchen".
Nu kun je zelf allerlei slimme truken gaan verzinnen (die waarschijnlijk niet zo slim zijn als je zelf denkt), of je gebruikt het werk van mensen met echt verstand van zaken op dit vlak. Een oplossing die bovendien al door vele andere slimmeriken tegen het licht is gehouden en tot nu toe als veilig beschouwd wordt:
bcrypt. Voor PHP is er het
Portable PHP Password Hashing Framework dat deze hashing methode aanbiedt.
Naast bcrypt biedt PHP ook een sha256/sha512-based crypt aan die behoorlijk veilig is, deze herhaald de hash een paar duizend keer om zo te voorkomen dat je met eenmalige hashing zit.
Daarnaast is het door RSA ontwikkelde
PBKDF2 nog inzetbaar, hoewel dat in theorie meer bedoeld is voor sleutels van willekeurige lengte (bijv een 2048-bits key genereren adhv een wachtwoord van willekeurige lengte).
Van andere methodes zoals
Secure Remote Password kreeg ik het idee dat het in beginsel best goed werkt, maar als nadeel heeft dat in web-based omgevingen het staat of valt met de rekenkracht die Javascript je biedt en daardoor is in de huidige implementaties de communicatie zelf relatief licht versleuteld. Als je die communicatie dan onderschept of de validatie-sleutel uit de database vindt is het alsnog eenvoudiger om te brute forcen dan domweg een paar duizend iteraties sha256 of bcrypt.